API reference
Minimal REST. Seven concept-endpoints, four resources, no GraphQL, no realtime. Base URL https://api.inbin.dev/v1
Authentication
Every request carries your application’s API key as a bearer token. Keys are issued at onboarding and can be rotated without downtime. Create a new key, migrate, revoke the old one.
curl https://api.inbin.dev/v1/inboxes \ -H "Authorization: Bearer ink_live_8we46jinhibs365yhztpo..."
Or use the official SDK, which wraps every endpoint and webhook verification:
npm i @inbin/core
import { Inbin } from "@inbin/core";
const inbin = new Inbin({ apiKey: process.env.INBIN_API_KEY });
const inbox = await inbin.inboxes.create({ name: "going-deals" });Resources
Four objects. Nothing more.
| Application | The tenant. Owns inboxes, schemas, webhooks, API keys. |
| Inbox | A unique, permanent email address, <slug>@in.inbin.dev. |
| Schema | What to extract. Versioned, monotonic, one active per application. |
| Event | The output. One per email received; immutable once created. |
Inboxes
Creating an inbox returns its address immediately. Slugs are six characters, globally unique. Deleting an inbox orphans its mail flow. Inbin still accepts, then drops.
POST /inboxes {"name": "chicago-forwards"}
{
"id": "ibx_a3f2c8",
"address": "a3f2c8@in.inbin.dev",
"application_id": "app_1234",
"created_at": "2026-07-23T20:15:00Z"
}Application
The caller’s application: name, webhook URL, plan, usage this month. PATCH accepts name and webhook_url, so CI can point Inbin at a fresh endpoint in the same pipeline that deploys it. The webhook secret is never returned. /health is unauthenticated and checks the database: 200 healthy, 503 not. Point your uptime monitor at it.
Schemas
JSON-Schema-like, deliberately constrained: no $ref, no oneOf, no recursion. A PUT replaces the schema and bumps the version. Invalid schemas are rejected at PUT time with a clear error. Old events are never re-parsed, the ledger is immutable. hallucination_guard (default on) drops any extracted value that does not appear in the email body.
PUT /schemas
{
"extract": {
"listings": {
"type": "array",
"items": {
"address": { "type": "string", "required": true },
"city": { "type": "string", "required": true },
"state": { "type": "string", "required": true, "pattern": "^[A-Z]{2}$" },
"zip": { "type": "string", "nullable": true, "pattern": "^\\d{5}$" }
}
}
},
"hallucination_guard": true
}Events
One event per email, whatever the extraction yielded. Status is one of pending, delivered, failed_delivery, dead_lettered. The raw email is kept 30 days behind a signed URL; extracted events are retained 90 days on Free and Builder, 365 days on Growth and Scale. Duplicate Message-IDs dedupe at ingest and return the same event.
Redeliver returns the same shape as a normal delivery: attempt number, response code, new status. Use it to replay dead-lettered events after fixing your endpoint.
Query
Your inbox as a table. Filter, sort, project, flatten array records, group and aggregate over everything your schemas extracted. One request, no database on your side. Also available to agents as the query_events MCP tool.
POST /query
{
"flatten": "deals",
"where": [{ "field": "price_usd", "op": "lt", "value": 300 }],
"group_by": "destination_city",
"aggregate": [{ "fn": "avg", "field": "price_usd", "as": "avg_price" }]
}
{ "rows": [
{ "destination_city": "Lisbon", "avg_price": 274.5 },
{ "destination_city": "Madrid", "avg_price": 289.0 }
],
"row_count": 2 }Ops: eq, neq, gt, gte, lt, lte, contains, in, exists. Aggregates: count, sum, avg, min, max. Limit caps at 200.
Webhook delivery
When an event lands, Inbin POSTs the event object verbatim to your webhook URL. Verify the HMAC before trusting the body.
Content-Type: application/json
X-Inbin-Event-Id: evt_9x8y7z
X-Inbin-Signature: sha256=... // HMAC of body with your secret
// verify with @inbin/core
import { verifyWebhook } from "@inbin/core";
const event = verifyWebhook(req.body, req.headers, secret);Retries back off exponentially. 30s, 2m, 10m, 1h, 6h, 24h. After six failures the event is dead-lettered. Redeliver it from the dashboard or via POST /events/:id/redeliver.
MCP
Every Inbin application is also an MCP server. Point an agent (Claude, ChatGPT, Cursor, custom) at https://inbin.dev/api/mcp with your Bearer API key and it can browse your parsed events without you writing a client. Transport is JSON-RPC 2.0 over HTTP (Streamable HTTP, MCP 2025-06-18).
| list_inboxes | Inboxes on this app. |
| list_events | Filter by inbox / status / since. Cursor paginated. |
| search_events | Case-insensitive substring across id / subject / from / to. |
| get_event | Full event body including extracted JSON and delivery history. |
| describe_schema | The current extraction schema for this app. |
curl https://inbin.dev/api/mcp \
-H "Authorization: Bearer ink_live_..." \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Connect from your MCP client
Two ways to talk to Inbin’s MCP surface:
- Remote (recommended): point your client at the URL directly with your API key as a Bearer header. No install, no local process.
- Local wrapper: install @inbin/mcpfrom npm and run it as a stdio server. Useful for clients that don’t yet support remote HTTP transport.
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json and add:
{
"mcpServers": {
"inbin": {
"url": "https://inbin.dev/api/mcp",
"headers": {
"Authorization": "Bearer ink_live_..."
}
}
}
}Restart Claude Desktop, then look for the “inbin” tools in the tools menu.
Cursor
Open Cursor Settings → MCP → Add. Paste:
{
"inbin": {
"url": "https://inbin.dev/api/mcp",
"headers": {
"Authorization": "Bearer ink_live_..."
}
}
}Windsurf, Zed, Cline
Any client that supports MCP’s Streamable HTTP transport takes the same shape. Endpoint + Authorization header. If your client only supports stdio, use the local wrapper:
# In your MCP client's server list
{
"inbin": {
"command": "npx",
"args": ["-y", "@inbin/mcp"],
"env": {
"INBIN_API_KEY": "ink_live_..."
}
}
}The wrapper reads INBIN_API_KEY and proxies stdio to the remote MCP endpoint.
Failure modes
| When | What happens |
|---|---|
| Model down / null extraction | Event is still created with extracted: null and an error. The webhook fires anyway. |
| Your endpoint is down | The retry queue holds events; the dashboard shows the backlog. |
| Broken schema | Rejected at PUT time. A broken schema is never accepted. |
| Duplicate email | Deduped by Message-ID; the webhook fires once. |
| Provider rate limits | Ingest queues; events deliver in order. |