The precise contracts behind the guides — authentication, scopes, errors, and rate limits. For the complete route-level schema, the OpenAPI document is the source of truth.
OpenAPI & base URLs
- API:
https://api.dirigex.ai - Console (humans):
https://app.dirigex.ai - Public catalog:
GET /v1/catalog/agents,GET /v1/catalog/agents/{id},POST /v1/catalog/agents/{id}/invoke(the unversioned/catalog/*aliases remain only for migration). - OpenAPI document: served anonymously at
GET /openapi.json— the authoritative, machine-readable contract for every route.
Authentication
| Scheme | Used for | Token |
|---|---|---|
| Cognito ID token | /self/* and /admin/* | The Cognito ID token in Authorization. The backend validates token_use="id" and rejects access tokens. |
| API key | Server-to-server | A dgx_<base64> bearer minted at POST /self/tenant/api-keys. |
Most common integration error: sending a Cognito access token where an ID
token is required →
401on a/self/*call. Decode the JWT and check
token_use.
API-key scopes
A key may carry any of these seven scopes:
| Scope | Grants |
|---|---|
catalog:read | Read the catalog |
agent:invoke | Invoke agents |
usage:read | Read usage |
metrics:read | Read metrics |
audit:read | Read audit events |
ingest:write | Register agents |
scim:provision | SCIM provisioning — Phase-1 surface only: Users.create, Users.read (no patch / bulk / filter / sort) |
Not grantable to API keys (by design): account-management capabilities such as manage_tenant_users and manage_approvals. DSAR export is Cognito-only and tenant-owner-gated — API-key bearers are rejected on that route.
Errors
All errors use a {code, message} envelope — no stack traces, no datastore internals, no half-populated records (the SDKs raise on malformed responses).
| Status | Meaning |
|---|---|
400 | Validation failure (missing/unknown field, unsupported value, unsafe input) |
401 | Missing or invalid token |
403 | Insufficient scope or membership |
404 | Not found in the caller's tenant scope — existence is masked for privacy (treat as "not visible to you," not "doesn't exist") |
429 | Rate limit exceeded |
The SDKs preserve both typed error codes and the raw server codes.
Rate limits
| Surface | Limit |
|---|---|
| Public catalog — list | 60 / 30 min per IP |
| Public catalog — detail | 60 / 30 min per IP |
| Public catalog — invoke | 20 / 10 min per IP |
| Admin routes | Separate buckets; configurable policy mode (off / log / enforce) |
| Tenant ingestion | Per-tenant limit on registration mutations |
Anonymous public-catalog callers are bucketed by XFF-first + TCP source to resist spoofing. The quota fields returned by /self/tenant/usage are advisory — enforcement is backend-owned, not derived from those fields.
Idempotency
| Route | Mechanism |
|---|---|
self.bootstrap() | Auto-generates an Idempotency-Key header if not supplied |
tenant.agents.invoke() | Optional idempotency-key + x-correlation-id headers |
tenant.agents.register() | JSON body field idempotencyKey (not a header) |
Re-sending the same idempotency context (or, for registration, the same canonical agent identity) is deduped rather than duplicated.
See also: Integrate the API · Build a governed agent · Trust & verification · documentation home.