Management API

Manage your EndPointBlank setup from your own code: API packages, clients, access, applications, environments and credentials. Everything the portal does for an organization admin, a management API key can do over JSON.

Key

Create a management API key

An organization admin creates one under Settings > API Keys and copies it once. Read-only keys can only GET. Management keys are not the client credentials your applications use at runtime, and runtime credentials are refused here.

Call

Send it as a Bearer token

Call https://app.endpointblank.com/api/v1 with Authorization: Bearer epb_mk_... and JSON bodies. GET /api/v1/organization shows which organization and key you are using.

Lists

Page with cursors

Lists return {data, next_cursor}. Pass ?limit= (1 to 100, default 50) and ?after=<next_cursor> until next_cursor is null.

Retries

Retry POSTs safely

Send an Idempotency-Key header on a POST. A retry with the same key and body returns the first answer instead of creating a second resource. Keys are kept for a day.

Errors

One error shape

Errors are {"error": {"code", "message", "details"}}. Programs should match on code, not message. Send Accept: application/json so errors raised before routing (an unknown path, a body that isn't JSON) also come back as JSON. Every code:

Authentication and limits

missing_key 401 No Authorization: Bearer header was sent.
invalid_key 401 The key is unknown, revoked or malformed.
runtime_credential_refused 401 Basic runtime credentials were sent. Send a management API key (epb_mk_...) as a Bearer token.
insufficient_scope 403 The key is read-only and the request writes.
audit_unavailable 503 The write could not be recorded in the audit log, so it was not made. Retry.
rate_limited 429 Too many requests. Wait for the Retry-After header's seconds.
plan_limit 402 Your plan's limit for this resource is reached.

Requests

validation_failed 422 One or more fields are invalid. details lists them by field.
not_found 404 No such resource in your organization, or no such /api/v1 path. Another organization's id answers this too.
invalid_pagination 400 limit is not 1-100, or after is not a next_cursor from an earlier page.
invalid_filter 422 A list filter has the wrong type, such as a non-UUID id.
invalid_idempotency_key 400 The Idempotency-Key header is empty or longer than 255 characters.
idempotency_key_reused 422 The Idempotency-Key was already used for a different request.
idempotency_request_in_progress 409 The first request with this Idempotency-Key is still running. Retry shortly.
idempotency_replay_unavailable 409 The first answer for this Idempotency-Key held a secret shown only once, so it can't be replayed. Read the resource instead.
bad_request 400 The request could not be read, such as a body that isn't valid JSON. Raised before routing.
internal_server_error 500 Something failed on our side. Safe to retry a GET.

Applications, environments and API packages

has_dependents 422 The application or environment still has grants or credentials. Remove them first.
protected 422 The production environment can't be changed or deleted.
invalid_environment_base_urls 422 environment_base_urls must map environment ids to base URLs.
api_package_assigned 422 The API package is still assigned to clients. Remove its assignments first.
intake_sync_failed 422 Intake would not take the change, so nothing was changed. message says why.

Credentials

delete_refused 422 Intake revoked the credential but its record could not be removed here. Retry the DELETE.
intake_credential 409 The credential belongs to an intake and can't be rotated or revoked here.
intake_rejected 422 On a credential, or on creating a managed client: intake refused the change, so nothing was changed. On DELETE /clients/{id}: intake answered a step of removing a managed client unexpectedly, the steps before it stay done, and sending the DELETE again carries it on.
intake_unavailable 503 Intake could not be reached or failed. Nothing was changed, except on DELETE /clients/{id}, where the steps of removing a managed client before it stay done. Retry.

Clients, packages and grants

invalid_contacts 422 contacts must be a list of objects with email, first_name and last_name, each email once.
invalid_packages 422 packages must be a list of objects with api_package_id and environment_id.
invalid_grants 422 grants must be a list of objects with target_application_id and environment_id.
invalid_managed 422 managed is not a boolean, managed: true came with packages or grants (assign those after creating the client), or owner_email came without managed: true.
already_invited 409 That address already has an invite from you that nobody has answered. Make a new link or revoke it.
invite_rate_limited 429 The organization has created as many email invites as it can in a day. Try again later.
not_an_email_invite 422 The client was invited with a code, or to an EndPointBlank account it already has, so it has no sign-up link.
invite_not_open 422 The email invite was accepted or declined, so it has no sign-up link.
invite_accepted 422 The client accepted its invite, so there is nothing to revoke. Remove the client instead.
client_not_accepted 422 The client hasn't accepted its invite yet, so nothing can be assigned or granted.
client_accepted 422 The client just accepted its invite and its setup is being applied. Nothing was changed.
client_not_managed 422 The client is not your managed client, or it was claimed.
client_being_removed 422 The managed client's removal started and stopped partway, so it can't be claimed or removed another way. Send DELETE /clients/{id} again to finish it.
already_a_member 422 That email address already belongs to the client.
return_to_not_registered 422 return_to is not exactly one of your organization's claim return URLs. Register it first.
return_url_not_registered 422 A portal session's return_url is not exactly one of your organization's claim return URLs. Register it first.
owner_email_missing 422 The managed client has no owner_email, so a portal session has nobody to sign in. Set one with PATCH /clients/{id}.
managed_client_has_credentials 422 The managed client still has credentials. Revoke them before removing it.
client_not_removable 422 The managed client holds something removing it does not remove, or intake refused a step of removing it. message says which; after a step, send the DELETE again once it is resolved.
api_package_not_found 422 api_package_id is not one of your API packages.
environment_not_found 422 environment_id is not one of your environments.
already_assigned 422 The client already holds that API package. Change its environment with PATCH.
nothing_published_in_environment 422 The package would give the client no grants in that environment. details lists why.
application_not_found 422 target_application_id is not one of your applications.
endpoint_not_found 422 target_endpoint_id is not an endpoint of target_application_id.
environment_not_in_application 422 environment_id is not one of the target application's environments.
already_granted 422 The client already holds that grant directly.
grant_revoked_concurrently 409 The grant was made, then revoked by another request. List the client's grants to see where it stands.

Invites

Invite clients by email

POST /api/v1/clients with an email invites that address instead of making a code. The answer's signup_url is shown once: EndPointBlank sends no invite email, so send it yourself. An admin of the client's organization with that confirmed address accepts it in the portal, or signs up from the link. POST /clients/{client_id}/invite/link makes a new link and DELETE /clients/{client_id}/invite revokes the invite. Without managed: true, the answer never says whether the address has an account.

Managed

Managed clients for people who already have an account

POST /api/v1/clients with managed: true and your customer's email creates a managed client, unless that address belongs to an EndPointBlank user. Then no managed client is created: the user is invited to the account they already have. Nothing is emailed and there is no signup_url; they accept or decline it on their Providers page, and it never expires. The answer is still a 201 with the client, but with managed: false and invite_status pending, so stop provisioning. Once they accept, the client is accepted like any other. It counts against your plan's client limit and the day's email invites, and a second request for the same address answers already_invited while the first is unanswered.

Claim

Send customers back after they claim

POST /api/v1/clients/{client_id}/claim_invites emails your customer an invite to claim their managed client. Add return_to to send their browser back to your app once accepting has claimed it, like an OAuth redirect_uri. return_to must equal, byte for byte, one of your claim return URLs, which an organization admin registers under Settings > API Keys: https (or http on localhost), no fragment, up to 10. Anything else answers return_to_not_registered, an empty return_to included: leave it out for none. The URL is checked again when the customer is redirected, so removing it stops invites already sent from using it; they land on EndPointBlank instead. A resend through the API replaces the invite's return_to, with none if it names none; a resend from the client's page in EndPointBlank keeps it. An acceptance that did not claim the client never redirects.

Portal

Open your customer's managed account portal

POST /api/v1/clients/{client_id}/portal_sessions answers 201 with data.url and data.expires_at: a link that signs your customer in to the managed account you run for them, where they see and revoke their connected AI agents, without an EndPointBlank login or an emailed code. Call it when they click through in your app and redirect their browser to url at once: it works once and expires after 60 seconds, and an expired or used link asks them to go back to your app. The client must be an unclaimed managed client of yours with an owner_email (owner_email_missing otherwise). The portal shows no link back to your app and no Sign out, since your customer stays in your app for both. An optional return_url is checked and echoed back: it must equal one of your claim return URLs, byte for byte, or the answer is return_url_not_registered. The url is never replayed for an Idempotency-Key retry; create another session instead.

Limits

Rate limits

Each key may make 600 requests a minute. X-RateLimit-Remaining counts down, and a 429 says when to retry in Retry-After.

Every operation, with its fields and responses, is in the management API reference. The same reference as an OpenAPI document is at /api/v1/openapi.json.