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
| Method | Path | Description |
|---|---|---|
| POST | /admin/projects | Create a project (+ initial API keys, kicks off provisioning). |
| GET | /admin/projects | List all projects owned by the developer. |
| GET | /admin/projects/:projectId | Project details + API keys + integrations. |
| PATCH | /admin/projects/:projectId | Update name, sign-in settings, default email sender, platform, environment. |
| DELETE | /admin/projects/:projectId | Delete the project: its database, secrets, API keys, and stored files. |
| POST | /admin/projects/:projectId/api-keys | Mint an additional API key. |
| DELETE | /admin/projects/:projectId/api-keys/:keyId | Deactivate an API key. |
| POST | /admin/projects/:projectId/reprovision | Re-drive a failed or stalled provision. |
| GET | /admin/projects/:projectId/provisioning-status | Poll the project's provisioning status. |
POST /admin/projects
Create a project.
Request
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Display name. |
bundle_id | string | no | Mobile 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_id | string | no | Google 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" | no | Defaults to "all". |
environment | "development" | "production" | no | Environment of the project's first key pair. Defaults to "production" (amb_live_ keys); "development" mints amb_dev_ keys. |
Response 201
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:
/admin/projectscurl -X POST 'https://api.amba.dev/v1/admin/projects' \
-H 'Content-Type: application/json' \
-d '{
"name": "My App"
}'Curl:
GET /admin/projects
List all projects owned by the authenticated developer, newest first.
Response 200
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:
/admin/projectscurl -X GET 'https://api.amba.dev/v1/admin/projects'Curl:
GET /admin/projects/:projectId
Fetch a single project with API keys (hashes only) and integrations.
Response 200
Errors
404 NOT_FOUND— project does not exist or is not owned by the developer.
Try it:
/admin/projects/%7B%7BprojectId%7D%7Dcurl -X GET 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D'Curl:
PATCH /admin/projects/:projectId
Update mutable fields. Unknown fields are silently ignored.
Request
| Field | Type | Notes |
|---|---|---|
name | string | |
bundle_id | string | Apple Sign In audience. Required if your app uses Sign in with Apple; otherwise signInWithApple returns 400 AUDIENCE_NOT_CONFIGURED. |
google_oauth_client_id | string | Google 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" | |
environment | string | Developer-chosen label. |
default_from_email | string | null | Default 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_name | string | null | Display name for the default sender (up to 80 characters). null clears it. |
default_reply_to | string | null | Default 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
Errors
400 INVALID_SENDER—default_from_emailis not on a verified sending domain of this project, or a sender field is malformed.404 NOT_FOUND.500 UPDATE_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7Dcurl -X PATCH 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D' \
-H 'Content-Type: application/json' \
-d '{
"name": "My App"
}'Curl:
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
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:
/admin/projects/%7B%7BprojectId%7D%7Dcurl -X DELETE 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D'Curl:
POST /admin/projects/:projectId/api-keys
Mint an additional API key.
Request
| Field | Type | Required | Allowed values |
|---|---|---|---|
key_type | string | yes | "client", "server" |
environment | string | yes | "development", "production" |
Response 201
The full key is returned ONCE and never again — the Amba API stores only a bcrypt-like hash.
Errors
400 INVALID_INPUT— badkey_typeorenvironment.404 NOT_FOUND.500 CREATE_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/api-keyscurl -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"
}'Curl:
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
Errors
404 NOT_FOUND.500 REVOKE_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/api-keys/%7B%7BkeyId%7D%7Dcurl -X DELETE 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/api-keys/%7B%7BkeyId%7D%7D'Curl:
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
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+.statusdescribe it.502 REPROVISION_FAILED— Amba's provisioning subsystem refused the request; the change was rolled back.500 REPROVISION_FAILED— unexpected error.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/reprovisioncurl -X POST 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/reprovision'Curl:
GET /admin/projects/:projectId/provisioning-status
Poll the current provisioning state for a project. Safe to poll every few seconds.
Response 200
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:
/admin/projects/%7B%7BprojectId%7D%7D/provisioning-statuscurl -X GET 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/provisioning-status'Curl: