Amba

Projects

Create, list, update, delete, and reprovision Amba projects; manage API keys.

Projects are the top-level unit of tenancy. Creating one provisions an isolated project, runs schema setup, seeds system segments, and mints an initial client + server API key pair.

Endpoints

MethodPathDescription
POST/admin/projectsCreate a project (+ initial API keys, kicks off provisioning).
GET/admin/projectsList all projects owned by the developer.
GET/admin/projects/:projectIdProject details + API keys + integrations.
PATCH/admin/projects/:projectIdUpdate name, sign-in settings, default email sender, platform, environment.
DELETE/admin/projects/:projectIdDelete the project: its database, secrets, API keys, and stored files.
POST/admin/projects/:projectId/api-keysMint an additional API key.
DELETE/admin/projects/:projectId/api-keys/:keyIdDeactivate an API key.
POST/admin/projects/:projectId/reprovisionRe-drive a failed or stalled provision.
GET/admin/projects/:projectId/provisioning-statusPoll the project's provisioning status.

POST /admin/projects

Create a project.

Request

POST /admin/projects
Authorization: Bearer <developer-access-token>
Content-Type: application/json
FieldTypeRequiredDescription
namestringyesDisplay name.
bundle_idstringnoMobile app bundle id (e.g. com.example.myapp). Doubles as the audience the server expects on Apple Sign In identity tokens — set this if your app uses Sign in with Apple.
google_oauth_client_idstringnoGoogle OAuth 2.0 client id. Doubles as the audience the server expects on Google Sign In id tokens — set this if your app uses Sign in with Google. Public identifier.
platform"ios" | "android" | "all"noDefaults to "all".
environment"development" | "production"noEnvironment of the project's first key pair. Defaults to "production" (amb_live_ keys); "development" mints amb_dev_ keys.

Response 201

{
  "data": {
    "project": {
      "id": "…",
      "name": "…",
      "developer_id": "…",
      "platform": "all",
      "tier": "free",
      "effective_tier": "free",
      "comped": false,
      "created_at": "…"
    },
    "api_keys": {
      "client": { "key": "amb_live_ck_…", "prefix": "amb_live_ck_", "type": "client" },
      "server": { "key": "amb_live_sk_…", "prefix": "amb_live_sk_", "type": "server" }
    },
    "provisioning_status": "provisioning"
  }
}

provisioning_status is "provisioning" if setup is in progress, "failed" if it didn't start (the project still exists — call POST /admin/projects/:projectId/reprovision to retry).

Errors

  • 500 CREATE_FAILED — Amba API write failed.

Try it:

POST/admin/projects
developer auth
curl -X POST 'https://api.amba.dev/v1/admin/projects' \
  -H 'Content-Type: application/json' \
  -d '{
  "name": "My App"
}'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X POST "${BASE_URL}/admin/projects" \
  -H "Authorization: Bearer ${DEV_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{"name":"My App"}'

GET /admin/projects

List all projects owned by the authenticated developer, newest first.

Response 200

{
  "data": [
    {
      "id": "…",
      "name": "…",
      "tier": "free",
      "effective_tier": "enterprise",
      "comped": true,
      "created_at": "…"
    }
  ]
}

tier is the persisted subscription/accounting classification and remains stable for backward compatibility. effective_tier is the runtime entitlement tier to use for quota or feature decisions. They normally match. A comped project reports effective_tier: "enterprise" because quota and spend-ceiling refusals are waived, while tier still shows the stored subscription class. comped: true makes the reason for the override explicit; it does not imply a paid Enterprise subscription.

Try it:

GET/admin/projects
developer auth
curl -X GET 'https://api.amba.dev/v1/admin/projects'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X GET "${BASE_URL}/admin/projects" \
  -H "Authorization: Bearer ${DEV_TOKEN}"

GET /admin/projects/:projectId

Fetch a single project with API keys (hashes only) and integrations.

Response 200

{
  "data": {
    "id": "…",
    "name": "…",
    "developer_id": "…",
    "bundle_id": "…",
    "platform": "all",
    "tier": "free",
    "effective_tier": "enterprise",
    "comped": true,
    "api_keys": [
      {
        "id": "…",
        "key_prefix": "amb_dev_ck_",
        "key_type": "client",
        "environment": "development",
        "is_active": true,
        "created_at": "…",
        "last_used_at": null
      }
    ],
    "integrations": [
      {
        "id": "…",
        "provider": "revenuecat",
        "is_active": true,
        "config": {
          /* provider-specific */
        }
      }
    ]
  }
}

Errors

  • 404 NOT_FOUND — project does not exist or is not owned by the developer.

Try it:

GET/admin/projects/%7B%7BprojectId%7D%7D
developer auth
curl -X GET 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X GET "${BASE_URL}/admin/projects/${PROJECT_ID}" \
  -H "Authorization: Bearer ${DEV_TOKEN}"

PATCH /admin/projects/:projectId

Update mutable fields. Unknown fields are silently ignored.

Request

FieldTypeNotes
namestring
bundle_idstringApple Sign In audience. Required if your app uses Sign in with Apple; otherwise signInWithApple returns 400 AUDIENCE_NOT_CONFIGURED.
google_oauth_client_idstringGoogle Sign In audience (OAuth 2.0 client id). Required if your app uses Sign in with Google; otherwise signInWithGoogle returns 400 AUDIENCE_NOT_CONFIGURED. Public identifier.
platform"ios" | "android" | "all"
environmentstringDeveloper-chosen label.
default_from_emailstring | nullDefault sender for project email and passwordless sign-in email (one-time codes and magic links). Must be on a verified sending domain of this project. If that domain later loses verification or is removed, email goes from the shared mailbox. null clears it.
default_from_namestring | nullDisplay name for the default sender (up to 80 characters). null clears it.
default_reply_tostring | nullDefault reply-to address for project email. null clears it.

An empty body is a no-op that returns the current row.

The sender fields take effect on the server, so your app code and client SDKs stay the same: sign-in codes and magic links use the default sender as soon as it is set.

Response 200

{ "data": { "id": "…", "name": "…", "updated_at": "…" } }

Errors

  • 400 INVALID_SENDER — default_from_email is not on a verified sending domain of this project, or a sender field is malformed.
  • 404 NOT_FOUND.
  • 500 UPDATE_FAILED.

Try it:

PATCH/admin/projects/%7B%7BprojectId%7D%7D
developer auth
curl -X PATCH 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D' \
  -H 'Content-Type: application/json' \
  -d '{
  "name": "My App"
}'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X PATCH "${BASE_URL}/admin/projects/${PROJECT_ID}" \
  -H "Authorization: Bearer ${DEV_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{"name":"My App"}'

DELETE /admin/projects/:projectId

Permanently delete the project. One call removes its database with all of its data, its secrets, the project with its API keys, and its stored files:

  • every media upload, in every storage bucket,
  • every file the project's functions wrote to storage,
  • every hosted site's deployed files, for each deployment the site kept.

This cannot be undone. The amba_projects_delete MCP tool calls this endpoint.

The stored files are deleted in the background, starting right after the delete answers, so the response never waits on them. Most projects' files are gone within seconds, and a project with very many files takes longer. A file that cannot be deleted at once is retried every few minutes, backing off (up to six hours between tries) while storage keeps refusing it, and the rest of the project's files go meanwhile. Six hours after the delete each location is checked once more, which removes a file that an upload or a site deploy still in flight at the delete wrote afterwards. Copies a CDN edge or a browser already cached expire with their cache lifetime (see Updates and cache freshness).

A project on hold or archived (see Unclaimed accounts) keeps every stored file; only a delete removes them.

A project on a paid plan has its subscription set to cancel at the end of its current period, so the period already paid for is the last one billed. This is the delete's first step: when the subscription cannot be set to cancel, the delete answers 502 BILLING_CANCEL_FAILED and nothing is deleted. A subscription you already canceled stays as it is, and a free project has no subscription to end.

The project's API keys stop working when the delete answers, the same way a revoked key does.

Response 200

{
  "data": {
    "deleted": true,
    "file_purge": { "locations": 2 },
    "subscription": { "ends_at": "2026-11-07T00:00:00.000Z" }
  }
}

file_purge.locations counts the stored-file locations whose deletion started: the project's storage (when it has any) and one per hosted site. 0 means the project had no storage and no hosted sites.

subscription.ends_at is when billing for the deleted project stops: the end of the current period. subscription is null when the project had no subscription still running.

Errors

  • 404 NOT_FOUND.
  • 409 FUNCTION_DOMAINS_ATTACHED: detach every custom function domain first.
  • 409 PROVISIONING_IN_PROGRESS: retry once provisioning settles.
  • 502 BILLING_CANCEL_FAILED: the project's subscription could not be set to cancel, and the project was not deleted. Nothing changed. Retry the delete, or contact support if it keeps failing.
  • 502 TENANT_RESOURCE_DELETE_FAILED: the delete stopped partway. The project, its API keys and its stored files stay, and its database may already be gone. A paid subscription is already set to cancel at the end of its current period. Retry the delete to finish it.
  • 500 DELETE_FAILED.

Try it:

DELETE/admin/projects/%7B%7BprojectId%7D%7D
developer auth
curl -X DELETE 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X DELETE "${BASE_URL}/admin/projects/${PROJECT_ID}" \
  -H "Authorization: Bearer ${DEV_TOKEN}"

POST /admin/projects/:projectId/api-keys

Mint an additional API key.

Request

FieldTypeRequiredAllowed values
key_typestringyes"client", "server"
environmentstringyes"development", "production"

Response 201

{
  "data": {
    "key": "amb_dev_ck_…",
    "prefix": "amb_dev_ck_",
    "type": "client",
    "environment": "production"
  }
}

The full key is returned ONCE and never again — the Amba API stores only a bcrypt-like hash.

Errors

  • 400 INVALID_INPUT — bad key_type or environment.
  • 404 NOT_FOUND.
  • 500 CREATE_FAILED.

Try it:

POST/admin/projects/%7B%7BprojectId%7D%7D/api-keys
developer auth
curl -X POST 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/api-keys' \
  -H 'Content-Type: application/json' \
  -d '{
  "key_type": "client",
  "environment": "development"
}'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X POST "${BASE_URL}/admin/projects/${PROJECT_ID}/api-keys" \
  -H "Authorization: Bearer ${DEV_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{"key_type":"client","environment":"development"}'

DELETE /admin/projects/:projectId/api-keys/:keyId

Deactivate (is_active = false) an API key. Revocation takes effect at the edge immediately: before this call answers, Amba's edge drops its cached copy of the key, so the next function call (*.fn.amba.host) presenting it fails 401. Calls to the Amba API itself refuse the key within 30 seconds.

An edge location that answered a request with the key in the 15 seconds before the revoke can accept it from its cached copy until that copy is 15 seconds old. If the edge cannot be reached when you revoke, the revoke still succeeds, Amba resends the eviction every minute, and every cached copy of the key stops being honored 15 seconds after it was cached.

Response 200

{ "data": { "revoked": true } }

Errors

  • 404 NOT_FOUND.
  • 500 REVOKE_FAILED.

Try it:

DELETE/admin/projects/%7B%7BprojectId%7D%7D/api-keys/%7B%7BkeyId%7D%7D
developer auth
curl -X DELETE 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/api-keys/%7B%7BkeyId%7D%7D'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X DELETE "${BASE_URL}/admin/projects/${PROJECT_ID}/api-keys/{keyId}" \
  -H "Authorization: Bearer ${DEV_TOKEN}"

POST /admin/projects/:projectId/reprovision

Re-drive a failed or stalled provisioning job. Idempotent — duplicate POSTs collapse onto the same job id. Concurrent calls serialize so two reprovisions can't run side-by-side.

Response 200

{
  "data": {
    "projectId": "…",
    "job_id": "reprovision-<project>-<ms>",
    "status": "provisioning"
  }
}

If a prior provisioning attempt is already running at the same millisecond, the response additionally includes "collapsed": true.

Errors

  • 404 NOT_FOUND — project not owned by the developer.
  • 409 ALREADY_ACTIVE — project is already active; reprovision is not permitted.
  • 409 ARCHIVED — archived projects cannot be reprovisioned.
  • 409 PROVISIONING_IN_PROGRESS — provisioning is already running. error.details.job_id + .status describe it.
  • 502 REPROVISION_FAILED — Amba's provisioning subsystem refused the request; the change was rolled back.
  • 500 REPROVISION_FAILED — unexpected error.

Try it:

POST/admin/projects/%7B%7BprojectId%7D%7D/reprovision
developer auth
curl -X POST 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/reprovision'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X POST "${BASE_URL}/admin/projects/${PROJECT_ID}/reprovision" \
  -H "Authorization: Bearer ${DEV_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{}'

GET /admin/projects/:projectId/provisioning-status

Poll the current provisioning state for a project. Safe to poll every few seconds.

Response 200

{
  "data": {
    "projectId": "…",
    "status": "active",
    "provisioned_at": "…",
    "region": "us-east-1"
  }
}

status is one of not_provisioned | provisioning | active | failed. provisioned_at is null until the project transitions to active. region is null for not_provisioned.

Errors

  • 404 NOT_FOUND.
  • 500 PROVISIONING_STATUS_FAILED.

Try it:

GET/admin/projects/%7B%7BprojectId%7D%7D/provisioning-status
developer auth
curl -X GET 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/provisioning-status'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X GET "${BASE_URL}/admin/projects/${PROJECT_ID}/provisioning-status" \
  -H "Authorization: Bearer ${DEV_TOKEN}"