Skip to main content

REST & GraphQL API

Authenticate to the Bex API, make REST and GraphQL requests, and handle workspace access, asynchronous operations, and errors.

Share
Last updated on September 23, 2026
On this page

Use the Bex API to automate service deployments, configuration, and operations. REST, GraphQL, and MCP share core operations, but their request shapes and available actions differ. Consult the guide for the operation you need; an endpoint accepting a request does not prove the resource is ready.

InterfaceHosted endpointUse
RESThttps://api.bex.co/v1/Scripts and integrations
GraphQLhttps://api.bex.co/graphqlStructured queries and dashboard operations
MCPhttps://api.bex.co/mcpAgent tools

For a self-hosted installation, use its API and OAuth origins. The operator-only quickstart does not set up all these services.

The Bex Website Content API describes public documentation and blog reads on bex.co. It is separate from these authenticated platform interfaces and does not describe service or database management. For the graded REST operation catalogue (method, path, summary, implementation grade) and interactive explorer, see the API reference. Platform contracts are maintained in the Bex API source.

Get a bearer token

Create an API key in the dashboard's API Keys settings while signed in as an eligible developer or admin. Save its id and secret when it is created; the secret is returned only once. A Bex API key is an OAuth client credential pair: the secret itself is not a bearer token.

Set BEX_KEY_ID and BEX_KEY_SECRET from your secret store, then exchange them:

bash
export BEX_API_URL='https://api.bex.co'
export BEX_OAUTH_TOKEN_URL='https://oauth.bex.co/oauth2/token'
 
BEX_TOKEN=$(curl --fail-with-body --silent --show-error \
  "$BEX_OAUTH_TOKEN_URL" \
  --data-urlencode 'grant_type=client_credentials' \
  --data-urlencode "client_id=$BEX_KEY_ID" \
  --data-urlencode "client_secret=$BEX_KEY_SECRET" \
  | jq -er '.access_token')
export BEX_TOKEN
 
curl --fail-with-body --silent --show-error \
  -H "Authorization: Bearer $BEX_TOKEN" \
  "$BEX_API_URL/v1/services"

This example requires curl and jq. Keep shell tracing off around credentials. Use the token response's expires_in to schedule another exchange; do not assume an access token is permanent. Client-credentials tokens are renewed by exchanging the key again, rather than by a browser login.

The API also accepts a valid dashboard session when no bearer credential is supplied. A supplied bearer credential takes precedence, including when it is invalid. Browser sessions and user-consented OAuth clients have different credential-issuance permissions from machine callers.

API-key management uses POST /v1/api-keys with name and optional ownerId, GET /v1/api-keys?ownerId=…, and DELETE /v1/api-keys/{id}?ownerId=…. Creating a durable key requires an authorized direct human caller; an existing machine key or third-party agent cannot mint another key for itself. Rotate by creating a replacement, updating consumers, and revoking the old key.

Workspaces and permissions

Machine API keys are bound to their workspace with the developer role. Creating one as an admin does not give the key admin permissions. Workspace administration, member invitations, GitHub connections, webhooks, and audit-log access require a suitable user credential instead. Use the dashboard's admin session or a user-authorized OAuth token with the required role and scopes for those operations; the client-credentials recipe above does not provide that role. See User-authorized OAuth integrations for a downloadable PKCE sample.

Use IDs returned by the API. Where an endpoint supports ownerId, supply the intended workspace explicitly, especially if your account belongs to several. By-ID requests must still pass authorization for the resource's workspace.

Workspace roles control resource access. Consented OAuth clients also need the relevant scopes: bex.read, bex.write, and/or bex.sensitive. A token scope does not grant a role the account lacks. Reading credentials, environment variables, or other sensitive values requires separate access from ordinary resource reads.

REST requests

Service listings use entries containing a service object and cursor. Follow the endpoint's pagination fields instead of treating one page as the whole workspace. Default page size is 20 and the maximum is 100. Pass the last entry's cursor as the opaque cursor query parameter; an unknown cursor returns an empty tail. Concurrent creates, deletes, or renames can change what later pages contain — complete traversal of returned pages is not a transactional inventory.

A runnable walker lives in /examples/service-inventory/ and is shared with the OAuth user client sample. The suspended field is a string enum: suspended or not_suspended.

Use the returned service id for subsequent operations:

bash
export BEX_SERVICE_ID='replace-with-service-id'
 
curl --fail-with-body --silent --show-error \
  -H "Authorization: Bearer $BEX_TOKEN" \
  "$BEX_API_URL/v1/services/$BEX_SERVICE_ID"

POST /v1/services creates a service; a name already in use is a conflict, not a redeploy. Use deploy operations for an existing service or the Blueprint workflow to converge a manifest. After an accepted mutation, inspect the deployment or resource until it reaches the expected state. Avoid blindly retrying creation after a network timeout: check whether the first request created the resource. For CI that deploys a tested container digest to an image-backed service, see CI deploys a tested image digest.

GraphQL requests

GraphQL has its own field names and input types. For example:

bash
curl --fail-with-body --silent --show-error \
  "$BEX_API_URL/graphql" \
  -H "Authorization: Bearer $BEX_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"query":"query { services { id type suspended url phase } }"}'

Inspect both data and errors: HTTP success alone does not establish that a GraphQL operation succeeded. Use the schema supported by your installed Bex version; Render's dashboard schema is not a compatibility guarantee for Bex.

Bex-only endpoints

Endpoints Bex adds beyond Render's public REST API are documented in their guides, not in the Render-parity API reference:

  • GET /v1/usage returns the calling workspace's month-to-date usage: per-resource meter totals, an advisory cost estimate, the billing projection for callers that may see it, and a coverage record (complete / partial / unknown) describing how complete the totals are. See Month-to-date usage. Implemented in lego/backend/internal/usage/rest.go (RegisterREST); the same verb also answers GraphQL Query.usage and the MCP get_usage tool.

Errors and compatibility

ResultNext step
400Inspect the error and check field names, types, and supported settings.
401Renew credentials; verify API origin and OAuth configuration.
402Payment method required for this create. A billing manager completes Billing setup; then retry.
403Check workspace, role, OAuth scopes, and any protected-environment requirement.
404Verify the endpoint and resource ID in the intended workspace.
409Resolve a name or state conflict before retrying.
429Honor Retry-After and back off; caller and workspace budgets can both apply.
503Check whether a required platform backend is unavailable or unconfigured.

Rate limits depend on installation configuration and the operation. They are not a universal promise of 500 requests per minute for each key.

Bex implements many Render-shaped endpoints plus Bex extensions. Authentication, unsupported fields, and operational behavior can differ; changing a Render client's base URL is not sufficient migration validation. Start with compatibility and migration from Render, then verify each operation your integration uses.

Was this page helpful?

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex