Documentation

API keys

Long-lived credentials for server-to-server calls — issuance, scoping, rotation, revocation.

API keys are AppEngine's answer to "I need a backend service to call the API without managing a JWT lifecycle". Keys are long-lived, scoped to a User principal, and sent on every request as the x-api-key header.

When to use a key vs a JWT

PatternUse JWTUse API key
Browser sessionyesno
Mobile app sessionyesno
Backend cron jobnoyes
Build pipelinenoyes
Server-rendered Next.js calling AppEngineusually JWT (per-user)API key for shared/system data
Webhook receiver in your code calling back into AppEngineyes (system user JWT)yes

JWTs encode a specific user session and expire. Keys are tenant-level credentials — they survive deploys, can be rotated without re-authenticating users, and carry their own rate limits.

Sending a key

GET /repository/find/contact HTTP/1.1
Host: appengine.appmint.io
orgid: my-org
x-api-key: ak_live_5f3a2b87e4c1...

orgid is still required. The JwtAuthGuard falls back to the ApikeyAuthGuard when no Bearer token is present.

Issuing a key

The Studio Manager UI is the easiest path: Account → API keys → "+ Create New Key". Programmatically:

POST/api-key/createJWT
curl -X POST https://appengine.appmint.io/api-key/create \
  -H "orgid: my-org" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production Web App",
    "description": "Used by the Next.js server",
    "permissions": ["write:profile", "write:data"],
    "rateLimits": { "perMinute": 100, "perHour": 1000, "perDay": 10000 },
    "expiresAt": null
  }'

The response includes the generated key string under data.key. Save it immediately — AppEngine stores only a hash; you cannot retrieve the plaintext key again.

Don't put keys in client code

Keys grant whatever permissions they were issued with — and unlike a JWT, they don't expire on their own. A leaked key can read or write the entire org until you revoke it. Server-side only.

Listing your keys

GET/api-key/listJWT

Returns metadata only (no plaintext) — name, description, scopes, rate limits, last-used timestamp. Use this to audit what's still active.

GET/api-key/{keyId}JWT

Single key by id, same metadata-only response.

Permissions and scopes

Each key has a permissions array. The strings follow the verb:resource pattern — read:data, write:data, read:profile, write:profile. AppEngine maps these onto the same permission verbs (create, read, update, delete) the role system uses; a key without write:data can't POST /repository/create regardless of which User backs it.

Common scope sets:

  • Read-only exportread:data, read:profile. Safe for analytics pipelines.
  • Storefront syncwrite:data scoped to specific collections (advanced setup, configured per-key).
  • Full backend — all verbs. The system-user pattern.

Rate limits

Per-key rate limits are enforced by UsageMiddleware. Defaults are 100/min, 1000/hour, 10000/day — generous for most server-to-server use, restrictive enough that a runaway loop won't take down your tenant.

The 429 response includes a Retry-After header. Honor it — repeated 429s within a short window count against your org's reliability score, which can affect rate-limit allowances over time.

Rotating a key

POST/api-key/regenerate/{keyId}JWT

Rotates in place: same name, same scopes, new key string. Use this for scheduled rotation. The old key continues working for a short grace period (configured per-org, default 24h) so you can deploy without downtime.

Revoking a key

DELETE/api-key/{keyId}JWT

Hard revoke. The key stops working immediately. Use this if you suspect a key is compromised.

For an admin acting on a key they didn't create:

POST/api-key/admin/revoke/{keyId}JWT

Requires RootAdmin or ConfigAdmin. Logged in the org's audit trail.

Updating metadata

PUT/api-key/{keyId}JWT

Update the name, description, scopes, or rate limits. The key string itself doesn't change. To change the secret, use regenerate.

Usage

GET/api-key/usage/{keyId}JWT

Returns request counts, error rates, and rate-limit hits over the configured window. Hook this into your monitoring.

Admin views

For ConfigAdmins managing org-wide keys:

GET/api-key/admin/listJWT

Cross-cuts all keys in the org, including ones created by other Users. The non-admin list endpoint only shows keys you created.

Per-org key endpoints

Some workflows (org provisioning, system-user setup) need a key tied to an org rather than a User. The org-scoped variants:

GET/api-key/org/{orgid}JWT
POST/api-key/org/{orgid}/createJWT
POST/api-key/org/{orgid}/deleteJWT

These are mostly used by the platform's own provisioning code. For application-level keys, use the User-scoped endpoints above.

Validating a key directly

Rare, but if you need to verify a key without making a real API call:

POST/api-key/authNo auth

Returns { valid: true|false, principal?: {...} }. Useful in custom proxies that want to enforce AppEngine auth before forwarding the request.

Pattern: system user

A widely-used pattern for legacy integrations: create a User record with a service-account email, give it the roles you need, and use its API key for backend calls. The key is then bound to that User's permissions, which makes audit trails clean ("contact updated by [email protected]") even though no human signed in.

When you create an org through the AppMint signup flow, a system User and a default API key are provisioned automatically. Find them in Studio Manager → Account → API keys.