API overview

Base URL, authentication, error responses, rate limits, and conventions.

The Agent Handler REST API is the management plane - your backend uses it to create Registered Users, manage Tool Packs, mint Link tokens, and configure the rest of your account. The MCP server is a separate surface (it’s what your agent talks to); see MCP integration for that side.

This page covers the conventions that apply across every endpoint. Per-endpoint reference is in the sidebar - Connectors, Credentials, Tool Packs, Registered Users, and so on.

Base URL

https://ah-api.merge.dev/api/v1

All paths in the reference are relative to this base.

Authentication

Every request needs an Access Key in the Authorization header:

Authorization: Bearer <YOUR_API_KEY>

The word Bearer is required. Tokens are not URL parameters - never put them in the path or query string.

Two key environments:

  • Production keys call against production data. One per organization.
  • Test keys call against a sandbox. Test Registered Users, test credentials, isolated audit log. Any number of test keys.

Mixing them produces 404 not_found on resources that exist in the other environment. Pick the right one for the call you’re making.

Errors

Every error response uses the same shape:

1{
2 "error": {
3 "type": "validation_error",
4 "code": "origin_user_id_required",
5 "message": "origin_user_id is required when creating a Registered User.",
6 "documentation_url": "https://docs.merge.dev/merge-agent-handler/resources/troubleshooting"
7 }
8}
HTTP statusWhen it fires
400 Bad RequestValidation failure. The code field names the specific check (origin_user_id_required, invalid_company_for_shared_scope). Don’t retry without fixing the input.
401 UnauthorizedMissing or invalid API key. Don’t retry - fix the credential.
403 ForbiddenAuthenticated but not allowed. Tool the Registered User can’t call, resource in the wrong environment, role missing the permission. Don’t retry.
404 Not FoundResource doesn’t exist (or exists in the other environment).
409 ConflictIdempotency conflict - e.g., creating a Registered User with an origin_user_id already taken in the same environment. The body includes the existing resource.
429 Too Many RequestsRate limit hit. Retry with backoff after the duration in the Retry-After header.
5xxAgent Handler issue. Retry with exponential backoff.

Errors are stable - when we add a new variant, we add a new code value rather than changing existing ones, so you can match on code safely.

Rate limits

Default limits per organization:

Endpoint familyLimit
Read endpoints (GET)600 requests per minute
Write endpoints (POST, PATCH, DELETE)120 requests per minute
MCP tool callsPer your plan; see Billing and usage

When you exceed a limit, the response is 429 with these headers:

Retry-After: 12
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1714838531

Honor Retry-After. Backing off by the suggested amount keeps you well-behaved; aggressive retry through a 429 will keep getting 429s until the bucket refills.

Custom rate limits are available on Business and Enterprise plans. Contact your account team if defaults aren’t enough.

Idempotency

POST endpoints that create resources are idempotent on a stable client-supplied key:

  • /registered-users - idempotent on origin_user_id. A repeat call with the same origin_user_id returns the existing Registered User (status 200), not a duplicate (status 201).
  • /registered-users/{id}/link-token - not idempotent. Each call mints a new link token; tokens are single-use and short-lived.
  • /credentials/export - not idempotent.

For endpoints that aren’t idempotent on a natural key, you can pass a request-scoped Idempotency-Key header with any unique value. Repeat requests with the same key within a 24-hour window return the original response without re-executing.

Idempotency-Key: 7b8e8c01-9c0e-44b3-b7a2-2b5d2f7d8e1c

Use UUIDs or any other globally-unique value. Don’t reuse keys across logically distinct calls.

Pagination

List endpoints are cursor-paginated. The response shape is consistent across resources:

1{
2 "data": [ /* up to `limit` items */ ],
3 "next_cursor": "eyJpZCI6ICJmOTgxM2RkNS0...",
4 "has_more": true
5}

Pass next_cursor as the cursor query parameter on the next request. When has_more is false, you’ve reached the end.

Default limit is 50; max is 250. The cursor is opaque - don’t try to decode or construct your own.

Versioning

The current API version is v1. New, backwards-compatible fields and endpoints are added without bumping the version. Breaking changes (renaming a field, removing an endpoint, changing semantics) ship under v2 - both versions run side by side during a migration window.

When breaking changes are coming, you’ll see them announced in the changelog with at least 90 days of notice and a migration guide.

Webhooks

Outbound webhooks fire from a separate domain (https://ah-api.merge.dev/webhooks/... is for inbound; outbound originates from Agent Handler to your endpoint). Verify signatures before trusting payloads - see Webhooks for the full guide.

SDKs

Official SDKs are in development. For now, the API is callable directly with any HTTP client - examples in the per-endpoint reference cover cURL, Python, and TypeScript.

For MCP-side integration (the agent’s view, not the management API), see MCP integration.

Status and support

API status, incidents, and uptime live at status.merge.dev. For support, file from the dashboard - include the request ID from the X-Request-ID header on the response and we can find your call in our logs immediately.