Skip to content
PostMTA Hosted
StatusSign inOpen panelRequest access
Open menu

Docs

Open in panel → /app · credits

Credits

PostMTA Hosted uses prepaid credit holds to gate SMTP and HTTP API sends. One credit covers one billable recipient (to + cc + bcc, deduplicated, case-insensitive).

RPC contract

Three Postgres RPCs, defined in supabase/migrations/20260712000000_credits_foundation.sql and extended in 20260712000001_credit_commit_void_membership.sql:

RPC Purpose Returns / errors
credit_hold(workspace_id, amount, idempotency_key, source) Reserve amount credits INSUFFICIENT, DUPLICATE
credit_commit(workspace_id, idempotency_key) Convert the hold into a debit HOLD_NOT_FOUND
credit_void(workspace_id, idempotency_key) Refund the hold (rejected recipient, upstream failure) HOLD_NOT_FOUND
credit_grant(workspace_id, amount, reason) Admin grant (panel Credits panel) n/a

Idempotency key uniqueness is enforced per workspace (unique(workspace_id, idempotency_key) on credit_ledger). The same idempotency key across hold → commit then a re-issued send returns 409 from the API without double-charging.

Operational flow

  1. POST /v1/send (or /v1/panel/send) validates from and recipient count.
  2. runSend in apps/api/src/app.ts calls credits.hold(workspaceId, amount, idempotencyKey, 'api').
  3. On INSUFFICIENT → 402, on DUPLICATE → 409.
  4. After Kumo inject returns 2xx → credits.commit. On non-2xx → credits.void.

Panel surface

Last verified: Sat Jul 11 2026 17:00:00 GMT-0700 (Pacific Daylight Time). Cross-link maintained byscripts/check-docs-drift.sh (M54).

← All docs