# API reference

The OpenAPI contract, then the designed endpoints.

**Base URL: https://sandbox.api.insimbi.xyz. The first part is the OpenAPI contract. Request access for the full specification.**

## In the OpenAPI contract

- **GET /health**: Liveness. Returns 204 with no body. No token needed.
- **POST /oauth2/token**: Exchange a signed client assertion for a short-lived Bearer token. Form fields: grant_type=client_credentials, client_id, client_assertion_type, client_assertion and an optional scope.
- **GET /.well-known/oauth-authorization-server**: Authorization server metadata: issuer, token endpoint, JWKS URI, the client_credentials grant, private_key_jwt client authentication and RS256.
- **GET /.well-known/jwks.json**: Public RS256 keys to verify tokens, each with a kid. During rotation more than one key is published.
- **GET /v1/organizations/{organization_id}**: Read an organization. Needs organization.read. Returns id, environment, environment_id and status.
- **GET /v1/legal-entities/{legal_entity_id}**: Read a legal entity. Needs legal_entity.read. Adds organization_id.
- **GET /v1/merchant-accounts/{merchant_account_id}**: Read a merchant account. Needs merchant_account.read. Adds organization_id and legal_entity_id.

_From the OpenAPI contract._

```
{
  "issuer": "https://sandbox.api.insimbi.xyz",
  "token_endpoint": "https://sandbox.api.insimbi.xyz/oauth2/token",
  "jwks_uri": "https://sandbox.api.insimbi.xyz/.well-known/jwks.json",
  "grant_types_supported": [
    "client_credentials"
  ],
  "token_endpoint_auth_methods_supported": [
    "private_key_jwt"
  ],
  "token_endpoint_auth_signing_alg_values_supported": [
    "RS256"
  ]
}
```

## Errors

Account reads return one error shape. The request_id matches the X-Request-ID header.

_From the OpenAPI contract._

```
{
  "error_version": "error/1.0",
  "code": "insufficient_authority",
  "message": "<message>",
  "request_id": "<request_id>"
}
```

- **401**: invalid_identity: The token is missing, expired or invalid.
- **403**: insufficient_authority: The token is valid but lacks the permission for this resource.
- **404**: not_found: The resource doesn't exist or isn't visible to you.
- **429**: rate_limited: Too many requests.
- **503**: unavailable: The service can't decide safely right now.

The token endpoint returns the OAuth error shape, {"error": "<code>"}, with Cache-Control: no-store. Codes: invalid_request, invalid_client, invalid_scope and unsupported_grant_type with 400, and temporarily_unavailable with 503. It also returns 405 for the wrong method and 429 when rate limited.

## Request IDs

Every response carries an X-Request-ID header. On token and account requests we always assign a fresh one. On other paths, an ID you send is kept if it is 1 to 128 letters, digits, dots, underscores or hyphens. Quote the request ID when you contact us, and never put secrets or personal data in it.

## Retries

Retry 429 and 503 with exponential backoff and jitter, and get a fresh token if yours has expired. Don't retry 400, 401, 403 or 404 unchanged: fix the request, the credentials or the grant first. When a decision can't be made safely, the API returns 503, never a cached allow.

## Designed endpoints

**Designed.** These endpoints are specified in our designs and are not in the OpenAPI contract yet. Paths and fields are illustrative and can change.

- **GET /v1/grants/{grant_id}**: Read an access grant.
- **POST /v1/payments**: Create a payment. Requires Idempotency-Key.
- **POST /v1/payments/{payment_id}/captures**: Capture an authorized payment.
- **GET /v1/transactions**: Search transactions by status.
- **GET /v1/tickets/{ticket_id}**: Read a ticket: fee lines with rule citations, decision and event references.
- **POST /v1/cutoffs/preview**: See what a cutoff change would do. Changes nothing.
- **POST /v1/exports/preview**: Start an export job.
- **GET /v1/exports/{job_id}**: Check an export job.
- **GET /v1/events**: List events with cursor pagination.
- **GET /v1/webhooks/{webhook_id}/deliveries**: List deliveries for an endpoint.
- **GET /v1/webhooks/{webhook_id}/deliveries/{delivery_id}/attempts**: See every attempt, status and timing.
- **POST /v1/webhooks/{webhook_id}/deliveries/{delivery_id}/replays**: Replay a delivery.

## POST /v1/payments: a payment, end to end

**Designed.** Payment endpoints are designed. Every check in this flow is written to the record.

**A payment, end to end.** Designed. Where the rule check, approval, rail choice and record happen.

Lanes: Your app or agent, insimbi API, Rules and mandate check, Operator, Rail, Record

1. Your app or agent to insimbi API: POST /v1/payments with an Idempotency-Key
2. insimbi API: Authenticate the token and record the access decision
3. insimbi API to Rules and mandate check: Check the payment against your rules and the agent's mandate
4. Rules and mandate check to Operator: Above your threshold: propose for approval
5. Operator to Rules and mandate check: A person approves
6. Rules and mandate check to insimbi API: Allowed. The rail rule chooses the rail.
7. insimbi API to Rail: Submit on the chosen rail
8. Rail to insimbi API: Rail result
9. insimbi API to Record: Write the ledger entry, fee lines cited to rules, and the ticket
10. insimbi API to Your app or agent: 201 with the ticket id
11. insimbi API to Your app or agent: Event delivered to your webhook endpoint

## Example: price a payment before it runs

Agents use the designed quote_ticket tool (see MCP tools). It returns the fee lines and the perimeter decision that would apply, without submitting anything.

_Illustrative example._

```
{
  "tool": "quote_ticket",
  "input": {
    "amount": "0.02",
    "currency": "USD",
    "payee": "api.example-data.com"
  }
}
```

_Illustrative example._

```
{
  "lines": [
    {
      "label": "network",
      "amount": "0.0004",
      "rule": "rule:rail.stablecoin.fee@v1"
    }
  ],
  "perimeter": {
    "id": "per_01J9",
    "version": 3,
    "decision": "allow"
  }
}
```
