HTTP API Reference
Use the TypeScript SDK for most integrations. Use direct HTTP when your environment cannot use the SDK or you need to make the requests yourself.
Authentication
Server-side calls use:
txt
X-API-Key: sk_...
X-Agent: your-agentAPI keys are issued from the developer dashboard after you sign in at https://configure.dev/login.
Linked or SSO-authenticated users send an approved Configure token. For OAuth SSO, keep the access token server-side. For Link fallback, use the agent-scoped token returned by Configure Link:
txt
Authorization: Bearer <configure-agent-token>Developer-scoped users send:
txt
X-User-Id: external-user-idConnector and web utility routes are token-authoritative: use X-API-Key plus an approved Bearer token. They do not accept X-User-Id or user_id request bodies.
Hosted Sign-In Routes
These routes back sign-in.me message-agent handoffs and hosted account-link callbacks. New web products with first-class browser sign-in should start with Configure OAuth. Message-based agents should use Message Agent SSO.
Hosted return-code callbacks return a short-lived code, never a JWT. Exchange that code on your server with a secret key. Message completions and phone recognition return or validate agent-scoped tokens that stay on your server.
| Method | Route | Auth | Purpose |
|---|---|---|---|
GET | /v1/auth/sign-in/agents/:agent/hosted | None | Resolve the canonical signInUrl, public branding, hosted metadata URL, and publishable key for an active agent. |
POST | /v1/auth/sign-in/return-destinations | X-API-Key: sk_... | Allowlist a web callback or native deep link for the acting agent. |
GET | /v1/auth/sign-in/return-destinations | X-API-Key: sk_... | List allowlisted return destinations. |
DELETE | /v1/auth/sign-in/return-destinations | X-API-Key: sk_... | Remove an allowlisted return destination. |
POST | /v1/auth/sign-in/code | Authorization: Bearer <configure-agent-token> | Configure-owned hosted page creates the return code after user approval. |
POST | /v1/auth/sign-in/exchange | X-API-Key: sk_... | Exchange one code for an agent-scoped token. |
POST | /v1/auth/sign-in/message-lines | X-API-Key: sk_... | Register a provider-owned message return line for the acting agent. |
GET | /v1/auth/sign-in/message-lines | X-API-Key: sk_... | List registered message return lines for the acting agent without raw phone numbers. |
DELETE | /v1/auth/sign-in/message-lines | X-API-Key: sk_... | Revoke a message return line for the acting agent. |
POST | /v1/auth/sign-in/message-url | X-API-Key: sk_... | Create a hosted message sign-in, reconnect, or permission-review URL. |
POST | /v1/auth/sign-in/recognize-phone | X-API-Key: sk_... | Recognize phone-like message sender IDs and return an agent token when approved. |
POST | /v1/auth/sign-in/validate | X-API-Key: sk_... | Validate a stored agent-scoped sign-in token for the acting agent. |
Every agent create, update, and list response also includes sign_in_url: "https://sign-in.me/{agent}". Send that value unchanged for the default hosted flow. The public hosted-agent response exposes the same value as signInUrl. Identity, credentials, and delivery behavior are resolved server-side.
POST /v1/auth/sign-in/return-destinations
json
{
"return_to": "https://app.example/auth/configure/callback"
}Return destinations must be absolute URLs or app links. https: is expected for production web callbacks. http: is accepted only for localhost development. URL fragments are stripped before storage.
POST /v1/auth/sign-in/exchange
json
{
"code": "cfgsic_..."
}Successful exchange returns the same token shape as Configure Link:
json
{
"token": "eyJ...",
"userId": "00000000-0000-0000-0000-000000000000",
"tokenUse": "agent",
"approved": true,
"agent": "your-agent"
}Codes expire after five minutes, are single-use, and are scoped to the developer account and acting agent resolved from the secret API key.
POST /v1/auth/sign-in/message-lines
Register the current provider-owned SMS/iMessage return line before including that phone in message-agent sign-in URLs. Prefer the SDK helpers (configure.auth.registerMessageLine(), listMessageLines(), and revokeMessageLine()) unless you are integrating at the raw HTTP layer. The endpoint requires a server-side sk_ key and binds the line to the acting agent resolved from that key. Put provider return phones in phone / messageLinePhone, not metadata. metadata is non-secret operator metadata only; raw phone fields, E.164 values, and NANP-style digit-only phone values are rejected.
json
{
"channel": "sms",
"phone": "+14155550123",
"label": "Primary SMS line",
"metadata": {
"provider": "your-sms-provider"
}
}Configure stores the phone hash and last4 only. Responses never include the raw phone number:
json
{
"line": {
"id": "11111111-1111-1111-1111-111111111111",
"channel": "sms",
"phoneLast4": "0123",
"label": "Primary SMS line",
"status": "active",
"metadata": {
"provider": "your-sms-provider"
},
"createdAt": "2026-07-02T17:00:00.000Z",
"updatedAt": "2026-07-02T17:00:00.000Z"
}
}GET /v1/auth/sign-in/message-lines returns { "lines": [...] } for active lines owned by the acting agent, including non-secret operator metadata. DELETE /v1/auth/sign-in/message-lines accepts the same phone and optional channel fields and returns { "deleted": true, "lines": [...] } for the revoked match. Revoked lines cannot be used in message-url requests until registered again.
POST /v1/auth/sign-in/message-url
Use this from a trusted message-agent server when the channel can provide stable sender and thread metadata. Configure returns a URL your agent can send in the message thread. The endpoint requires a server-side sk_ key.
json
{
"reason": "signin",
"channel": "sms",
"subject": {
"key": "sms:sender:abc123",
"externalId": "sms:sender:abc123",
"senderId": "abc123"
},
"thread": {
"key": "space-123",
"spaceId": "space-123",
"messageId": "message-456"
},
"messageSenderProof": "provider-signed-message-sender-proof",
"connectors": ["gmail", "calendar"],
"agentPhone": "+14155550123",
"messageBody": "done",
"messageCompleteUrl": "https://agent.example.com/auth/configure/complete",
"journeyId": "space-123:message-456",
"idempotencyKey": "space-123:message-456:signin"
}agentPhone is the current provider-owned return line for the agent. messageLinePhone is accepted as the lower-level equivalent. The supplied phone must be registered as an active message line for the acting agent and channel. messageCompleteUrl and journeyId let the hosted flow call your completion endpoint and map the browser handoff back to the message thread. When messageSenderProof is missing or unsupported, Configure returns the plain hosted handoff, preserves the supplied message return metadata, and does not create a code-bearing link:
json
{
"mode": "plain",
"url": "https://sign-in.me/your-agent?delivery=message&message_line_phone=%2B14155550123&message_body=done&message_complete_url=https%3A%2F%2Fagent.example.com%2Fauth%2Fconfigure%2Fcomplete&journey=space-123%3Amessage-456",
"reason": "signin",
"fallbackReason": "sender_proof_missing",
"idempotencyKey": "space-123:message-456:signin"
}Today the endpoint always responds with mode: "plain" plus a fallbackReason of sender_proof_missing or sender_proof_unsupported. A code-bearing minted response shape is reserved for verified message sender proof and is not returned yet.
For connector repair, send the same request with reason: "reconnect" and the affected connector list. If Configure cannot verify sender proof yet, the response remains plain and points at the hosted reconnect surface:
json
{
"mode": "plain",
"url": "https://sign-in.me/your-agent/reconnect?connectors=gmail&delivery=message&message_line_phone=%2B14155550123&message_body=done",
"reason": "reconnect",
"fallbackReason": "sender_proof_unsupported",
"idempotencyKey": "space-123:message-456:reconnect:gmail"
}Do not put message URL creation in a model prompt or expose this endpoint to the browser. The agent runtime or adapter should decide when to send the link before the model handler runs.
POST /v1/auth/sign-in/recognize-phone
Use this from a trusted message-agent server when the channel provides sender metadata. Configure normalizes tel:, sms:, imessage:, raw E.164, and digit-only phone candidates. It also accepts exact email-form iMessage handles and recognizes them only when the same address is an OAuth-verified Gmail or Outlook credential for the user. It never matches inferred, synthesized, or user-typed profile email.
json
{
"candidates": ["imessage:+14155551234", "+1 (415) 555-1234", "person@icloud.com"]
}If the phone or connector-verified email belongs to exactly one Configure user and the acting agent is already approved, the response includes an agent-scoped token:
json
{
"matched": true,
"recognized": true,
"approved": true,
"linked": true,
"token": "eyJ...",
"agentToken": "eyJ...",
"userId": "00000000-0000-0000-0000-000000000000",
"agent": "your-agent",
"displayName": "Example User",
"phoneCandidateCount": 2
}If Configure recognizes the user but they have not approved the agent, matched and recognized are true while approved and linked are false. Do not treat recognition alone as authorization. Send a hosted sign-in.me link instead.
POST /v1/auth/sign-in/validate
Use this server-side when restoring a hosted sign-in session or accepting a message completion webhook. Do not validate agent-scoped SSO tokens through /v1/me; that route accepts user-scoped JWTs only.
json
{
"token": "eyJ..."
}The token is valid only if it is an unexpired Configure agent token, belongs to the developer account and agent resolved from your sk_ key, and the user still has an approved agent relationship.
json
{
"valid": true,
"userId": "00000000-0000-0000-0000-000000000000",
"tokenUse": "agent",
"approved": true,
"agent": "your-agent"
}Profile Routes
| Method | Route | SDK method |
|---|---|---|
GET | /v1/profile | profile.read() |
GET | /v1/profile/search | profile.search() |
POST | /v1/profile/remember | profile.remember() |
POST | /v1/profile/forget | profile.forget(selector, { date?, reason? }) |
POST | /v1/profile/import | profile.import() |
POST | /v1/profile/connect | profile.connect() |
POST | /v1/profile/commit | profile.commit() |
POST | /v1/profile/mcp-session | profile.mcpSession() |
Legacy memory aliases are not mounted.
GET /v1/profile accepts sections, box, and page query parameters. box opens a category, source, or projects/<slug> box instead of returning the composed profile, matching the MCP read's box path. page paginates a box open.
GET /v1/profile/search accepts query, source, box, from, to, limit, and detail query parameters. For source, the bare provider names claude, gemini, grok, chatgpt, and other are ambiguous and match both the agent's own memories and the imported memories. Use agents/<name> or imports/<provider> for an exact scope.
POST /v1/profile/forget
The body carries exactly one selector: id (the mem_... id returned by the save), match (words a memory contains, with optional confirm: true to delete past the preview), import_id (undo one whole import), or scope (imports, saved, or all, to clear the agent's own writes in bulk). date and reason (correction or user_request) are optional. Forget deletes memories in the acting agent's own memories only. reason: "user_request" also suppresses matching content from other sources. Full parity with the MCP configure_profile_forget tool; the SDK reaches the same selectors through profile.forget(selector, { date?, reason? }).
POST /v1/profile/mcp-session
Exchanges your developer credential plus one user for a short-lived MCP session, so an MCP-capable agent runtime holds its own connection to Configure for that user instead of proxying tool calls through your backend.
bash
curl -X POST https://api.configure.dev/v1/profile/mcp-session \
-H "X-API-Key: $CONFIGURE_SECRET_KEY" \
-H "X-Agent: your-agent" \
-H "X-User-Id: your-user-id"json
{
"ok": true,
"linked": true,
"continued": false,
"session": { "token": "eyJhbGciOi...", "expires_at": "2026-08-19T23:46:32.396Z" },
"mcp_url": "https://api.configure.dev/mcp",
"mcp_servers": [
{ "type": "url", "url": "https://api.configure.dev/mcp", "name": "configure", "authorization_token": "eyJhbGciOi..." }
]
}mcp_servers is the block Anthropic's mcp_servers parameter takes verbatim; any other MCP runtime wants mcp_url plus the token as a bearer. The token is the whole credential: one bearer, no extra headers.
Sessions last 15 minutes. Mint one per conversation and let it expire; never store it, and never hand it to a browser. An expired session answers 401 with suggested_action: "reauthenticate", so mint a new one rather than retrying. This TTL is the point: your secret key is the only durable credential, so revoking it stops every future session at once.
Refreshing a session that outlived its token. A runtime that runs longer than 15 minutes (a daemon, a worker, a long agent loop) mints again and passes the token it is replacing as previous_token:
bash
curl -X POST https://api.configure.dev/v1/profile/mcp-session \
-H "X-API-Key: $CONFIGURE_SECRET_KEY" \
-H "X-Agent: your-agent" \
-H "X-User-Id: your-user-id" \
-H "Content-Type: application/json" \
-d '{"previous_token": "eyJhbGciOi..."}'The new session then keeps the old one's read/commit correlation, so a configure_profile_read done before the refresh can still be cleared by a configure_profile_commit after it. Without it the read is orphaned: the commit matches nothing, and the memory-poisoning defence that leans on those obligations goes quiet. continued: true in the response confirms the carry.
The rules are deliberately blunt. An expired or unreadable previous_token is not an error: the session mints fresh with continued: false, because a runtime that let its token lapse has no live read waiting to be committed. A valid token for a different user, agent, developer, or key plane is a 400: that is token confusion in your code, not an expiry, and it should be loud. Pass session.token, not the session object.
Refreshing never extends anything: each mint is a fresh 15 minutes under your secret key, and the scope-age gate still forces a commit cadence on a long-lived session. In the TypeScript SDK, profile.mcpAuth() does all of this for you: one provider per user runtime, await auth.mcpServers() before every model call.
The session inherits the plane of the key that minted it. A sk_test_ key mints a sandbox session that resolves only sandbox users; a live key mints a live session. Mixing them resolves nothing and the call fails.
linked: false means no approved user was resolved for this agent. The session still works, but every personal tool fails closed and only configure_connect runs, which is enough for the agent to mint a sign-in link in its own voice. Wire that path: it is the connect flow, not an error.
Requires a secret key on every path. Publishable keys are rejected: the minted session can write, and a pk_ key is public.
Bulk Import Routes
Bulk import is server-side only and requires a secret key. It is not a model tool, does not use profile.commit(), and does not use X-Configure-* turn-correlation headers.
| Method | Route | SDK method |
|---|---|---|
POST | /v1/import/profiles | configure.importProfiles() |
GET | /v1/import/jobs/:jobId | configure.importJobs.get(jobId) |
POST | /v1/import/jobs/:jobId/cancel | HTTP only |
POST /v1/import/profiles
Headers:
txt
X-API-Key: sk_...
X-Agent: your-agent
Content-Type: application/jsonBody:
json
{
"mode": "backfill",
"idempotencyKey": "migration-2026-05-11",
"users": [
{
"externalId": "customer-123",
"profile": {
"summary": "Longtime traveler based in SF.",
"preferences": ["Prefers window seats."]
},
"conversations": [
{
"id": "thread-1",
"messages": [
{ "role": "user", "content": "I usually fly out of SFO." },
{ "role": "assistant", "content": "Got it." }
]
}
]
}
]
}New jobs return 202 with a durable job. Replaying the same idempotencyKey returns 200 with the existing job:
json
{
"id": "imp_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"mode": "backfill",
"status": "queued",
"accepted_profiles": 1,
"estimated_message_count": 2,
"estimated_input_chars": 48
}Request bodies may include externalId, conversations, profile hints, mode, and idempotency keys. Request-level idempotency is scoped to the developer account and acting agent. Bodies may not include storage locations, source, written_by, timestamps, or memory IDs. Where the memory is stored, source, and written_by are derived from the server-resolved agent.
Import Storage
Import writes typed memories under:
txt
/agents/{resolved-agent}/memories/{YYYY-MM-DD}/{mem_id}.jsonImport records live under:
txt
/agents/{resolved-agent}/imports/{import_job_id}/...Import does not write root profile files such as /identity.json, /preferences.md, /user.md, or /context.md.
Import Limits
| Tier | Unique imported profiles | Max profiles/job | Max input chars/job | Concurrent import jobs |
|---|---|---|---|---|
| Free | 100 | 25 | 2M | 1 |
| Pro | 1,000 | 250 | 20M | 2 |
| Scale | 10,000 | 1,000 | 200M | 5 |
| Enterprise | Unlimited | 10,000 default chunk | 200M direct API default; larger by staged contract | 10 default; contract-managed |
Global caps: 250 conversations/profile, 1,000 messages/conversation, and 20,000 characters/message.
| Status | Code | Meaning |
|---|---|---|
402 | quota_exceeded | Unique imported-profile quota would be exceeded. |
413 | payload_too_large | The payload exceeds a job/input cap. |
422 | invalid_format | The shape is invalid or includes forbidden metadata. |
429 | rate_limited | Concurrent import job cap reached. |
Native MCP
Two MCP surfaces exist. The personal OAuth endpoint (https://mcp.configure.dev) lists the six profile tools after sign-in (configure_profile_read, configure_profile_search, configure_profile_remember, configure_profile_forget, configure_profile_commit, configure_profile_import); configure_connect appears only before sign-in, and connector or action tools are never listed there. API-key (developer) servers list configure_connect plus the six profile tools, and add the connector and action tools (configure_gmail_search, configure_email_send, configure_calendar_get, configure_calendar_create_event, configure_drive_search, configure_notion_search) only when the request carries a linked-user token. Listing is not permission: what runs is enforced live, per call. A call for an app that is not connected fails closed with an error; mint the link with configure_connect. Only scope and permission failures return a minted link directly in the result. When the runtime enables the outlook connector, the server also exposes configure_email_search (Gmail plus Outlook) and an Outlook-capable configure_email_send.
A third way in is POST /v1/profile/mcp-session (above): it hands your runtime a single-bearer session for one of your users, so an MCP-capable framework connects natively instead of proxying tool calls through your backend. That session lists the same developer-server surface described here, connector and action tools included, because it carries a linked-user token.
Use the package MCP server when you need its local opt-in extras (Sheets, files, web).