What lives here
git.askapi.ca/v1/*
GitDrive — repositories, files, packages, search, sharing. Forgejo-backed git protocol on the same host paths under /{owner}/{repo}.git.
mail.askapi.ca/v1/*
SmartMail — mailboxes, messages, threads, contacts, ingest webhooks, search, AI assistance.
Internal product UIs (git.askmail.ca, mail.askmail.ca) call their own backends same-origin under /v1/* with cookie auth — those endpoints are NOT for external consumption. The askapi.ca surfaces are stable, versioned, and bearer-authenticated.
Authentication — OAuth2 client_credentials
For server-to-server agents and headless tools. Get a JWT, send it as Authorization: Bearer … on every request.
- Register an OAuth2 application in Authentik at login.ask-ai.ca (admin-only). Capture
client_id+client_secret. - POST to the token endpoint with
grant_type=client_credentials. - Authentik returns an
access_token(JWT, RS256-signed) valid for ~60min. - Send
Authorization: Bearer <access_token>on every API call. - Refresh before expiry by repeating step 2.
Get a token
curl -X POST https://login.ask-ai.ca/application/o/token/ \
-d grant_type=client_credentials \
-d client_id=YOUR_CLIENT_ID \
-d client_secret=YOUR_CLIENT_SECRET \
-d scope=openid
# Response: {"access_token":"eyJhbGc...","token_type":"bearer","expires_in":3600}
Call the API
curl -H "Authorization: Bearer $TOKEN" https://git.askapi.ca/v1/me
curl -H "Authorization: Bearer $TOKEN" https://mail.askapi.ca/v1/mailboxes
Python (requests)
import requests
tok = requests.post(
"https://login.ask-ai.ca/application/o/token/",
data={"grant_type": "client_credentials",
"client_id": "YOUR_ID", "client_secret": "YOUR_SECRET",
"scope": "openid"}
).json()["access_token"]
r = requests.get("https://git.askapi.ca/v1/me",
headers={"Authorization": f"Bearer {tok}"})
print(r.json())
Browser SPAs (PKCE)
For browser apps that aren't same-origin with the API, use OAuth2 Authorization Code with PKCE. The mig-auth.js v2 SDK implements this — see MIGAuth.initOIDC() + MIGAuth.apiFetch().
Token claims
Tokens are RS256-signed JWTs. Validation: signature against https://login.ask-ai.ca/application/o/<app-slug>/jwks/; issuer matches; audience matches your client_id; expiry valid.
{
"iss": "https://login.ask-ai.ca/application/o/<app>/",
"sub": "<user-or-service-uid>",
"aud": "<client_id>",
"email": "...",
"preferred_username": "...",
"groups": ["..."],
"exp": 1777254387,
"iat": 1777250787
}
Errors
| Code | Meaning |
|---|---|
401 | Missing, malformed, expired, or signature-invalid token. |
403 | Token valid but missing required scope or group. |
404 | Resource not found. Path-typo or you don't own it. |
429 | Rate-limited. Back off. |