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.