Skip to content

API reference

The Splitjar API is a single Cloudflare Worker exposing JSON over HTTPS. Every route lives under https://splitjar.app/api/.

Authentication

Two authentication modes:

  • Cookie session (sj_session): set by the magic-link flow. Used by the SPA at splitjar.app/app/*.
  • Reply-edit token (HMAC, body-embedded): used by the email reply path only. Not a general-purpose API token.

There is no public bearer-token API at this stage. If you need programmatic access, contact hello@splitjar.app.

Rate limits

Two layers stack in front of every route:

LayerScopeWhere
Cloudflare WAFPer IPEdge — see scripts/cf-rate-limits.mjs
In-app KVPer emailWorker — applied per-route in src/lib/auth.ts

The WAF is volumetric (per-IP, coarse). The KV is precise (per-email, intentional). Both can fire on the same request.

Auth & sessions

MethodPathPurpose
POST/api/auth/startAtomic: upsert user → session → group → invites → magic link. Returns { group: { slug } }.
POST/api/auth/magic-linkSend a sign-in link to an existing user.
GET/api/auth/callback?token=…Verify magic-link, set sj_session cookie, flip verified_at.
POST/api/auth/signoutClear sj_session.
GET/api/meCurrent user’s profile + entitlement view.
PATCH/api/meUpdate display name, locale.

Jars (groups)

MethodPathPurpose
POST/api/groupsCreate a jar.
GET/api/groups/:idOrSlugRead jar with members + entitlement.
PATCH/api/groups/:idOrSlugOwner-only: rename, change default split, change base currency.
POST/api/groups/:idOrSlug/membersOwner-only: add a member by email.
PATCH/api/groups/:idOrSlug/members/:userIdOwner-only: rename a member (subject to the cross-tenant guard).
DELETE/api/groups/:idOrSlug/members/:userIdOwner-only: remove a member. 409 if they have ledger artifacts.

Receipts

MethodPathPurpose
POST/api/ingestWeb upload of a receipt (multipart).
GET/api/groups/:idOrSlug/receiptsList receipts in the jar.
GET/api/receipts/:idRead one receipt with extraction + splits.
PATCH/api/receipts/:idEdit fields. Re-enqueues fx + finalise.
POST/api/receipts/:id/approveMove out of needs_review.
DELETE/api/receipts/:idSoft-delete (status flip, ledger preserved).

Billing

MethodPathPurpose
POST/api/billing/checkoutStripe Checkout session for Jar or Year.
POST/api/billing/portalStripe Customer Portal link.
POST/api/billing/webhookStripe webhook ingress (signature-verified).

Errors

All errors share the shape:

{
"error": {
"code": "forbidden_personalised_name",
"message": "This member has set their own display name. Only they can change it.",
"details": {}
}
}

The code is stable and machine-readable. The message is locale-resolved per the recipient’s users.locale.

Conventions

  • All times are Unix milliseconds.
  • All amounts are numbers (not strings), denominated as the receipt’s currency for the original and the jar’s base_currency for the converted figure.
  • Slugs are 6 characters of base32, prefix-free with the jar id.