Concepts
Carte is a typed, addressable mailbox for businesses. These are the nouns you will meet in the API and the dashboard.
Business
The account that owns an inbox, and the only kind of party. One business has one handle, its members (the people who act for it), any number of agents (each with its own API keys) and zero or more verified domains. A sole trader is a business with one member. Nothing about a business is derived from a domain: it is created by a claim, and a domain is an attribute it earns.
Member
A person's place in a business. The person who claims the handle is the owner; everyone else is a member. Membership comes from one of two proofs: the Google Workspace domain the person signed in from matches a domain the business has verified (they join automatically on sign-in), or an owner invites them (coming later). Members read the whole inbox and send as the business; the owner also manages keys, webhooks, members, domains, the handle and the schema. A person can belong to several businesses; the dashboard shows the one they own, else the oldest.
Verified domain
A domain a business has proven it controls. Today the proof is the Google Workspace sign-in (hd claim) of the owner: a Workspace claimer's domain is verified on the spot, and the owner can verify it later from Settings. DNS verification for other providers is coming. A domain belongs to at most one business. It shows as verified_domains on the party in every envelope, receipt and contact, and is what lets colleagues join on sign-in.
Segment and target
A member or an agent can have a segment: a name inside the business, with the same rules as a handle (2–32 lowercase letters, digits or hyphens) and unique across the business's members and agents. It makes the member or agent addressable directly: to: "acme/bob", to: "acme/finance-agent", carte.sh/acme/finance-agent or acme+finance-agent@in.carte.sh. Every envelope then carries to.target, and the sender's own segment appears on from.actor.segment.
// Addressed to acme/bob: a member of acme
{ "kind": "member", "id": "usr_…", "segment": "bob", "name": "Bob" }
// Addressed to acme/finance-agent: an agent of acme
{ "kind": "agent", "id": "agt_…", "segment": "finance-agent", "name": "Finance Agent" }
// Addressed to acme/nobody: nothing matched, the message still landed in acme's inbox
{ "kind": "unknown", "id": null, "segment": "nobody", "name": null }
// Addressed to acme
nullA target is routing, not privacy. The party is still the business and every member reads the whole inbox. Targets filter the inbox (GET /v1/inbox?to=bob), webhooks (a webhook created with a target only fires for that segment) and the MCP tools. An unknown segment still delivers to the business, with the raw segment recorded, because losing a message is worse than a bad address. A reply defaults its target to the parent's actor when that actor is a member or agent of the receiving business, so a reply to what acme/finance-agent sent goes back to acme/finance-agent.
Handle
The business's address, claimed once at /claim. It appears in the drop link (carte.sh/acme), the email address (acme@in.carte.sh) and as the to.party of every message it receives.
Party
Who a message is from or to, in the business sense. Every envelope has from.party and to.party. A party is either a claimed handle or an unclaimed organisation identified by its verified email domain. People are never parties: a person always sends on behalf of one.
// A claimed handle, with the domains the business has verified
{ "kind": "handle", "handle": "acme", "display_name": "Acme Corp", "verified_domains": ["acme.com"] }
// An unclaimed organisation, identified by its verified email domain
{ "kind": "domain", "domain": "northwind.example", "display_name": "northwind.example" }
// A consumer mailbox is identified by its full address
{ "kind": "domain", "domain": "jane@gmail.com", "display_name": "jane@gmail.com" }A Google Workspace account sends on behalf of its domain with no onboarding. A consumer Google account (gmail.com and similar) has no domain to stand for, so it must claim a handle before it can send (party_required). A person who owns a business always sends as that business, including from drop pages.
Actor
The hand that sent a message on behalf of its party: from.actor. kind is person (Google sign-in, id is a usr_ id), agent (an API key, id is an agt_ id) or mailbox (email-in, id is null). provider is google, api_key or email.
// A person who signed in with Google
{ "kind": "person", "id": "usr_…", "name": "Jane Doe", "email": "jane@northwind.example", "verified": true, "provider": "google", "segment": "jane" }
// An agent sending with an API key
{ "kind": "agent", "id": "agt_…", "name": "invoice-bot", "email": null, "verified": true, "provider": "api_key", "segment": "invoice-bot" }
// A mailbox that emailed handle@in.carte.sh (verified = DKIM pass)
{ "kind": "mailbox", "id": null, "name": "Jane Doe", "email": "jane@northwind.example", "verified": true, "provider": "email", "segment": null }There are no anonymous sends. actor.verified is true when the identity was proven: Google sign-in, a valid API key, or an email that passed DKIM.
Agent
A named sender inside a business: a name, a description and an optional segment, created in the dashboard under Agents & keys. API keys are minted under an agent, and every API send is attributed to the agent (actor kind agent), never to the person who created the key. Keys rotate; the agent stays. GET /v1/me returns the agent behind the current key, and GET /v1/agents lists every agent with its segment so a caller can address a colleague agent.
Inbox and outbox
The inbox is everything received by the handle (GET /v1/inbox). The outbox is everything the business sent (GET /v1/outbox). Both are lists of message envelopes, newest first, and both accept thread=thr_… to narrow to one thread.
Message
One delivery. A message has an optional type (a short string like invoice), an optional subject and note, an optional JSON body, and zero or more files. Every read returns the same envelope shape shown below.
Thread
Every message belongs to a thread. thread_id (thr_…) is the root message's id with the prefix swapped; in_reply_to is the exact message a reply answers, or null for a root. A thread has exactly two parties and is flat: GET /v1/threads/{thread_id} returns it oldest first. Reply with in_reply_to on POST /v1/messages; the reply goes to the other party of the parent, so to is optional.
A business can reply to an unclaimed party but never initiate contact with it. The reply stays in Carte: the person who started the thread gets a notification email with no message content, and reads the reply in their inbox once they claim a handle.
Contact
The address book entry for a party. Every party you have exchanged messages with appears in GET /v1/contacts on its own, with activity derived from the ledger: first and last seen, counts in and out, the message types and the actors seen behind it. Saving a contact adds your own label and notes, which every agent on the business reads before sending. Contacts are parties, never people, and they are archived rather than deleted.
Drop link
https://carte.sh/<handle>. A public page where a sender pastes JSON or drags files, signs in with Google, and gets a receipt. The message is from their business or Workspace domain, with them as the actor.
Email-in
<handle>@in.carte.sh. Subject and body become subject and note, attachments become files, and the full email lands in json.email. The party is the sender's domain; the actor is the mailbox. See Email-in.
Receipt
Every message has a public receipt at receipt_url. It is the envelope without the JSON body and without download URLs: the message ID, timestamp, party and actor, and SHA-256 hashes of the JSON and each file. Senders keep it as proof of delivery.
Ledger
Messages and files are append-only. Nothing updates or deletes them, and no endpoint exposes deletion. content_hash covers the whole message; json_sha256 and each file's sha256 cover the parts.
Webhook
An HTTPS URL that receives a signed POST of the envelope for every message delivered to your inbox, replies included. See Webhooks.
API key
A ck_live_… bearer token minted under an agent in the dashboard. Scope is read (inbox, messages, files, threads) or read_write (everything, including sending, replying, webhooks, blocks and the inbox schema). Keys are shown once and can be revoked; sends made with a key are attributed to its agent.
Envelope
The shape returned by every read, by POST /v1/messages, and in every webhook body.
{
"id": "msg_01J8ZK3Q9V7N4X2M6P8R5T1W3Y",
"thread_id": "thr_01J8ZK3Q9V7N4X2M6P8R5T1W3Y",
"in_reply_to": null,
"from": {
"party": {
"kind": "handle",
"handle": "northwind",
"display_name": "Northwind Traders"
},
"actor": {
"kind": "agent",
"id": "agt_01J8ZK3QA1B2C3D4E5F6G7H8J9",
"name": "invoice-bot",
"email": null,
"verified": true,
"provider": "api_key",
"segment": "invoice-bot"
}
},
"to": {
"party": {
"kind": "handle",
"handle": "acme",
"display_name": "Acme Corp",
"verified_domains": ["acme.com"]
},
"target": {
"kind": "agent",
"id": "agt_01J8ZK3QB2C3D4E5F6G7H8J9K0",
"segment": "finance-agent",
"name": "Finance Agent"
}
},
"type": "invoice",
"subject": "Invoice #1042",
"note": null,
"json": { "total": 1240.5, "currency": "USD" },
"json_sha256": "sha256:3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b",
"files": [
{
"id": "file_01J8ZK3QA1B2C3D4E5F6G7H8J9",
"filename": "invoice-1042.pdf",
"content_type": "application/pdf",
"size_bytes": 48211,
"sha256": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"download_url": "https://storage.example/…",
"download_expires_at": "2026-09-14T10:15:00.000Z"
}
],
"validation": { "status": "not_applicable", "errors": [] },
"received_at": "2026-09-14T10:00:00.000Z",
"content_hash": "sha256:5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8",
"receipt_url": "https://carte.sh/r/rcpt_01J8ZK3QB2C3D4E5F6G7H8J9K0",
"idempotency_key": "invoice-1042"
}jsonis omitted on public receipts;download_urlanddownload_expires_atare only present on authenticated reads and are short-lived.validation.statusispassed,failedornot_applicable, depending on whether the inbox has a JSON Schema set.sha256on a file isnulluntil hashing completes (files over 100 MB are hashed asynchronously).