POST /api/v1/inbox
Ingest an external commitment as a Pending_Handshake Shrubbery. The row appears immediately in the assignee's Accord; Inngest fires a Nudge through the assignee's preferred channel. The token owner is recorded as the assigner.
Quick start
curl -X POST https://shrubbery.eu/api/v1/inbox \
-H "Authorization: Bearer $SHRUBBERY_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"assignerEmail": "lead@example.com",
"assigneeEmail": "knight@example.com",
"description": "Ship the Q3 report",
"deadline": "2026-09-30T17:00:00.000Z",
"externalSystem": "jira",
"externalId": "PROJ-123"
}'A successful response is 201 Created with the new Shrubbery's id.
Reference
- Try it interactively:
/api-reference#tag/webhooks/post-api-v1-inbox— Scalar's sample switcher (curl / TypeScript / Python / Ruby / Go) + Test Request client with Bearer-token paste. - Payload field table: Webhooks → Inbox Payload — byte-for-byte against the Zod schema.
- OpenAPI JSON:
content/docs/_generated/openapi.json— auto-emitted from Zod, no manual drift.
Notes
- Assigner = token owner. Inbox tokens cannot mint rows on behalf of other users. The
assignerEmailfield exists for human readability of the audit trail; the resolved user must matchtoken.user_id. - External mapping uniqueness. Two leads may independently track the same external ticket (
(externalSystem, externalId, assigner)is unique, not(externalSystem, externalId)alone). A replay from the same token returns409 duplicate_external_ref. - Nudge fan-out. Inngest's
handshakeRequestedEventfires once per successful insert. Channel selection (email / Teams / Google Chat) is governed by the assignee's notification preferences. Delivery failures are retried by Inngest, not by the inbox route. - No partial parsing. If any field fails Zod validation the whole payload is rejected — no fields persist.
Last updated: 17 May 2026