This page is the source-of-truth for what the public API exposes today. Each row notes the auth model, the forensic audit event the route emits (when relevant), and the ezToken cost (for billable actions). Routes marked stub return 501 and are reserved for v0.2.
Contents
Shared concepts (auth, ezTokens, errors)
git.askapi.ca — GitDrive
mail.askapi.ca — SmartMail
Observability — /health, /metrics, audit events
External dependencies (per service)
Changelog
Shared concepts
Authentication
Mode Use case Header
Bearer JWT External agents, partner integrations, headless tools Authorization: Bearer <jwt> — RS256, validated against Authentik JWKS at https://login.ask-ai.ca/application/o/<app>/jwks/
Forward-auth Internal UI calls (same-origin git.askmail.ca/v1/*, mail.askmail.ca/api/v1/*) x-authentik-username + x-authentik-uid + x-authentik-groups injected by traefik
Webhook secret Internal/system calls (Forgejo push, backup heartbeat) X-Gitcloud-Secret: <shared> or matching Authorization
Public /boot/*, /health, /metrics, /share/{id}None
ezTokens
Billable actions are denominated in ezTokens (1 token = 0.0001 CAD). The token is deducted via ASK-Ledger before the action runs; on failure the deduction is refunded automatically. Bundles purchased via POST /v1/tokens/purchase (CIBCx checkout). Balance: GET /v1/tokens/balance.
Error codes
Code Meaning
401Missing or invalid auth (Bearer JWT fails signature/issuer/audience/expiry, or no forward-auth headers)
402Insufficient ezToken balance — body includes balance + required
403Tenant mismatch (token scope ≠ resource owner) or share-link forbidden
404Resource not found, or share-link expired/revoked
429Rate limit hit — body has Retry-After seconds
501Stub endpoint reserved for v0.2 (CI, code review LLM, RAPTOR, backup snapshots)
502/503Upstream service unavailable (Forgejo, RustFS, Milvus, RuVector, Ledger)
git.askapi.ca — GitDrive
Boot & health
Method Path Auth Audit event Purpose
GET /bootpublic — v0.2.0 addon descriptor with capability-group pointers
GET /boot/{auth,billing,code,media,packages,search,webhooks,infra}public — Per-capability contract docs
GET /healthpublic — Liveness (200 always when up)
GET /health/fullpublic — Deep probe: SurrealDB, Milvus, RuVector, RustFS, Ledger + backup freshness
GET /metricspublic LAN — Prometheus exposition format — scraped by CT 700 MIG-Observability
Identity
Method Path Auth Audit event Purpose
GET /v1/meBearer/Forward — Echo caller's auth context (uid, email, username, tenant, groups)
DELETE /v1/meBearer/Forward audit.account.erasedRight-to-erasure (PIPEDA/GDPR): cascades Forgejo repos, SurrealDB rows, Milvus/RuVector collections, RustFS objects. Keeps Ledger txns (CRA 7y rule), Authentik user, ASK-Mail mailbox.
Code (Forgejo-backed)
Method Path Auth Audit event Cost / notes
GET /v1/code/reposBearer/Forward — List user's repos
POST /v1/code/reposBearer/Forward audit.repo.createdPRICE_CODE_REPOS_CREATE JIT-provision Forgejo user, register ingest webhook
GET /v1/code/repos/{owner}/{repo}/contents/{path+}Bearer/Forward — Read file at default branch or ?ref=sha
POST PUT /v1/code/repos/{owner}/{repo}/contents/{path+}Bearer/Forward audit.contents.writtenPRICE_CODE_CONTENTS_WRITE POST=create, PUT=update (sha required)
GET /v1/code/repos/{owner}/{repo}/commitsBearer/Forward — Filter by since / path / author
GET /v1/code/repos/{owner}/{repo}/diff/{base}...{head}Bearer/Forward — Unified diff + files-changed summary
GET /v1/code/repos/{owner}/{repo}/aboutBearer/Forward — PRICE_CODE_REPOS_ABOUT Semantic metadata (description, topics, entities) from ingest pipeline
Media (RustFS S3)
Method Path Auth Audit event Cost / notes
GET /v1/media/{tenant}/{user}/{path+}Bearer/Forward — 302 → presigned RustFS GET URL (1hr TTL)
PUT /v1/media/{tenant}/{user}/{path+}Bearer/Forward audit.media.uploadedmax(PRICE_MEDIA_UPLOAD_MIN, ceil(MB) × PRICE_MEDIA_UPLOAD_PER_MB) 60/min rate limit
DELETE /v1/media/{tenant}/{user}/{path+}Bearer/Forward audit.media.deleted—
Sharing
Method Path Auth Audit event Notes
POST /v1/media/shareBearer/Forward audit.share.created10/min rate limit; configurable expiry (max 10y)
GET /v1/sharesBearer/Forward — List own share-links
DELETE /v1/shares/{share_id}Bearer/Forward audit.share.revokedRevoke share-link
GET /share/{share_id}public — Public 302 → RustFS presigned (15 min TTL)
Search
Method Path Auth Cost / notes
POST /v1/search/codeBearer/Forward PRICE_SEARCH_CODE Forgejo repo-name search; 60/min
POST /v1/search/semanticBearer/Forward PRICE_SEARCH_SEMANTIC Hybrid Milvus dense (BGE-M3 1024-d) + RuVector text via RRF
POST /v1/search/unifiedBearer/Forward GitDrive + SmartMail cross-product search
POST /v1/search/raptorBearer/Forward stub 501 — RAPTOR multi-level summaries reserved for v0.2
Tokens & billing
Method Path Auth Audit event Notes
GET /v1/tokens/balanceBearer/Forward — Caller's ezToken balance from ASK-Ledger
GET /v1/tokens/bundlesBearer/Forward — Proxy ASK-Axum-Pay bundle catalog
POST /v1/tokens/purchaseBearer/Forward audit.purchase.initiated5/min rate limit; returns CIBCx checkout URL
GET /v1/tokens/usageBearer/Forward — Proxy ASK-Ledger transaction history
Packages (multi-format registry via Forgejo)
Method Path Auth Notes
GET /v1/packages/{owner}Bearer/Forward List all packages owned by {owner}
GET /v1/packages/{owner}/{type}/{name}Bearer/Forward List versions
GET /v1/packages/{owner}/{type}/{name}/{version}Bearer/Forward Get package metadata
DELETE /v1/packages/{owner}/{type}/{name}/{version}Bearer/Forward Delete version
Publishing uses native package-manager clients against Forgejo directly: npm set registry https://git.askmail.ca/api/packages/{owner}/npm/, pip install --index-url https://git.askmail.ca/api/packages/{owner}/pypi/simple/ ..., docker push git.askmail.ca/{owner}/{image}:{tag}. Git LFS at git.askmail.ca/{owner}/{repo}.git/info/lfs (RustFS bucket gitcloud-lfs).
Webhooks (outbound)
Method Path Auth Audit event Cost
GET /v1/webhooks/{owner}/{repo}Bearer/Forward — —
POST /v1/webhooks/{owner}/{repo}Bearer/Forward audit.webhook.createdPRICE_WEBHOOK_REGISTER
DELETE /v1/webhooks/{owner}/{repo}/{hook_id}Bearer/Forward audit.webhook.deleted—
Audit
Method Path Auth Notes
GET /v1/audit/run/{run_id}Bearer/Forward Fetch ingest run + all pipeline events (extract / classify / embed / raptor)
Internal & legacy
Method Path Auth Notes
POST /internal/ingest/commitwebhook secret Forgejo push webhook → Pulsar topic gitcloud-ingest
GET /internal/content/{hash}none LRU-trimmed chunk cache for STier crawl
POST /internal/backup/heartbeatwebhook secret Backup script heartbeat → SurrealDB backup_heartbeat:{source}
GET /v1/repos, /v1/files/{path}, /v1/commitBearer/Forward Legacy aliases — proxy to canonical paths (ASK-Mail compat)
Stubs (501, reserved v0.2)
Path Will deliver
/v1/ci/repos/{owner}/{repo}/runs · /v1/ci/runs/{run_id} · /v1/ci/runs/{run_id}/logsCI runner integration
/v1/backup/{tenant}/{user}/snapshots · /v1/backup/.../restorePer-tenant snapshot/restore
/v1/review/llmCode review LLM
mail.askapi.ca — SmartMail
Boot & health
Method Path Auth Purpose
GET /bootpublic Self-describing addon contract — 13 capabilities
GET /healthpublic Liveness
GET /metricspublic LAN Prometheus exposition
Mail (WildDuck-backed)
Method Path Auth Audit event Notes
GET /api/v1/meBearer/Forward — Caller identity
GET /api/v1/me/addressesBearer/Forward — List user's email addresses
GET /api/v1/mailboxesBearer/Forward — IMAP folder list
GET /api/v1/mailboxes/{mb}/messages?page=&pageSize=Bearer/Forward — Page through messages (max 100)
GET /api/v1/mailboxes/{mb}/messages/{id}Bearer/Forward audit.mail.readMarks seen, normalizes HTML
PUT /api/v1/mailboxes/{mb}/messages/{id}/flagsBearer/Forward — Toggle seen / flagged
DELETE /api/v1/mailboxes/{mb}/messages/{id}Bearer/Forward audit.mail.deleted—
POST /api/v1/messages/submitBearer/Forward audit.mail.sentResolves sender's primary address; posts to WildDuck submit
POST /api/v1/searchBearer/Forward — BM25 full-text via RuVector
POST /api/v1/ingest/emailBearer/Forward audit.mail.received1 ezToken/email Full pipeline: TimescaleDB INSERT → STier signals → SurrealDB+TimescaleDB write → BGE-M3 embed → RuVector upsert → Ledger charge → usage event
POST /api/v1/ingest/batchBearer/Forward per-email audit.mail.received Loops ingest_email; returns array of {ok, result|error}
AI
Method Path Auth Audit event Notes
POST /api/v1/chatBearer/Forward audit.ai.chatRAG pipeline: RuVector recall → ai_prefs filter → EvolveRouter (basic→chat / analysis→thinking)
POST /api/v1/ai/chatBearer/Forward audit.ai.chatSession-aware variant; persists to SurrealDB chat_session_msg
GET /api/v1/ai/prefsBearer/Forward — List per-folder AI privacy
PUT /api/v1/ai/prefs/{folder}Bearer/Forward audit.ai.pref_changedSet folder enabled/disabled
DELETE /api/v1/ai/prefs/{folder}Bearer/Forward audit.ai.pref_changedReset to default (enabled)
POST /api/v1/vectors/ensure_collectioninternal — Milvus mail_{username} create
POST /api/v1/vectors/indexinternal — Pre-computed embedding upsert
POST /api/v1/vectors/searchinternal — COSINE ANN
Domains & tenancy
Method Path Auth Audit event Notes
POST /api/v1/admin/tenantsBearer (admin) audit.tenant.createdTenant record
GET /api/v1/admin/tenants/{id}Bearer (admin) — —
GET /api/v1/domainsBearer/Forward (tenant-scoped) — —
POST /api/v1/domainsBearer (admin) audit.domain.registeredWildDuck dominalias + DKIM selector
POST /api/v1/domains/{fqdn}/verifyBearer (admin) audit.domain.verifiedDNS TXT lookup (deferred — currently sets verified=true; trust-dns-resolver wiring is v0.2)
GET /api/v1/domains/{fqdn}/usersBearer/Forward — List users on domain
POST /api/v1/domains/{fqdn}/usersBearer (admin) audit.user.createdWildDuck user create
Storage proxy (to GitDrive)
Method Path Auth Notes
GET POST PUT DELETE /api/v1/storage/repos · /api/v1/storage/files/{path} · /api/v1/storage/commitBearer/Forward Forwards Authentik headers to gitcloud-api; tier gating enforced upstream
Internal
Method Path Auth Notes
GET /internal/email/{email_id}/bodyLAN-only 5-min TTL cache; serves email body to STier crawl
Observability
Endpoint What
/healthLiveness — 200 if process up. Both APIs.
/health/full(GitDrive only) Deep dependency probe. 503 if SurrealDB / RustFS / RuVector / Ledger / backups unhealthy.
/metricsPrometheus text exposition. Scraped by CT 700 MIG-Observability. Counters: http_requests_total{route,status}, auth_failures_total, ledger_deductions_total{reason}, ledger_failures_total{reason}, audit_events_total{kind}.
journald JSON tracing-subscriber emits structured records. Promtail ships to Loki. Critical events marked event=audit.*.
Forensic event taxonomy (current)
Event Emitted by Fields
audit.auth.failedboth APIs · auth.rs::from_bearer when JWT invalid reason=bearer_invalid
audit.ledger.deductedgitcloud-api · ledger.rs::deduct on 200 actor, tokens, reason, balance
audit.ledger.insufficientgitcloud-api · ledger.rs::deduct on 402 actor, tokens, reason
audit.mail.receivedask-mail-api · ingest.rs::ingest_email actor, email_id, from, signal_count, tokens_charged
More audit events to be wired before v0.1.0 tag : audit.repo.created, audit.contents.written, audit.media.uploaded, audit.media.deleted, audit.share.created, audit.share.revoked, audit.account.erased, audit.mail.sent, audit.mail.deleted, audit.domain.registered, audit.user.created, audit.tenant.created, audit.ai.chat, audit.ai.pref_changed.
External dependencies
gitcloud-api (CT 235 .235:8700)
Service CT Used by
Forgejo 228 (.121:3000) repos, contents, commits, packages, code search, account-erasure
RustFS 232 (.232:9000) media get/put/delete, share-link redirect
SurrealDB (canonical) 230 (.230:8000) indexed_chunk, raptor_node, ingest_run, ingest_event, media_share, backup_heartbeat
BGE-M3 embed 100 (.100:8097) semantic_search, ingest worker
RuVector (text) 231 (.229:8102) semantic_search, ingest worker, account-erasure
Milvus (dense) 307 (.228:19530) semantic_search, ingest worker, account-erasure
STier 131 (.18:8300) ingest classify (signal extraction)
ASK-Ledger 251 (.251:8092) deduct / refund / balance / usage
ASK-Axum-Pay 252 (.252:8093) token bundle catalog
CIBCx external checkout URL (proxy only)
Pulsar 306 (.157:8080) gitcloud-ingest topic (durable, Shared sub)
Authentik (JWKS) 719 (login.ask-ai.ca) JWT validation (1hr cache)
ask-mail-api (CT 730 .231:3000)
Service CT Used by
WildDuck (IMAP) 730 (localhost:8080) mailboxes, messages, submit, addresses, domain users
SurrealDB 731 (.233:8000) tenant, domain, email_signal, ai_prefs, chat_session_msg, user_plan
TimescaleDB / Postgres 736 (.168:5432) email, contact, email_signal_ts hypertables (7y retention, 3mo compression)
RuVector (BM25) 735 (.164:8102) per-user mail search index
Milvus 734 (.234:19530) vector index (BGE-M3 1024-d HNSW)
RustFS 732 (.240:9000) email attachments + archives
STier 131 (.18:8300) signal extraction during ingest
BGE-M3 embed 100 (.100:8097) email body embedding
EvolveRouter 100 (.100:8091) RAG chat completion
ASK-Ledger 251 (.251:8092) 1-token-per-email charge
Authentik (JWKS) 719 (login.ask-ai.ca) JWT validation
GitDrive API 235 (.235:8700) storage proxy (tier-gated upstream)
Changelog
2026-04-27 — v0.1.0 readiness pass: /metrics endpoints added, JSON tracing, structured audit events (audit.auth.failed, audit.ledger.deducted/insufficient, audit.mail.received) wired with counter inc(). SQL-injection fix in mail ingest. Auth-migration to AuthCtx::auth().await across all routes. Body cache TTL.
2026-04-27 — askapi.ca + askmail.ca product-domain rollout (per doc:gitdrive_url_swap_20260426). Hybrid auth: cookie internal, Bearer JWT external.