POST /v2/contributions is an asynchronous version of the v1 contribution submission endpoint. It takes exactly the same request body, but accepts much larger batches: it validates the request straight away, returns a transactionId with 202 Accepted, and submits to the clearing house in the background. The outcome arrives by webhook (or you can poll for it).
When to use v2 vs v1
Use v2 when your pay runs exceed the v1 limits, or when you’d rather not split one pay run into many v1 requests. v1 is unchanged and still available — you don’t have to migrate.
How it works
- Submit —
POST /v2/contributionswith yourIdempotency-Keyheader. - Validated up front, all or nothing — we check the payload, that the employer exists and is onboarded at the clearing house, that every employee exists for your client, the MVR payment gate (same rules as v1), and that every
usiis an active fund. If anything fails you get a400listing every error, and nothing is stored. 202 Accepted— you get back atransactionId. This is the one SuperChoice transaction ID used everywhere afterwards: payments, payment status endpoints, andpayment.status_changedwebhooks.- Background submission — the submission is usually picked up within about a minute and sent to the clearing house as one transaction (one direct debit).
- Outcome — the
contribution.submission_completedwebhook fires, or pollGET /v2/contributions/:transactionId. - After
SUBMITTED— everything else (payment status,payment.status_changedwebhooks, fund responses) works exactly as it does for v1 transactions.
Idempotency-Key
TheIdempotency-Key header is required. Generate a unique key for each new submission (a UUID, or something derived from your own pay-run ID), store it, and send the same key again if you retry the same request — for example after a timeout or network error.
Keys are scoped to your client account and never expire, so never reuse a key for a different submission.
Validation errors
A400 lists every business-rule problem in the request at once, each with the path of the field that caused it, so you can fix everything in one go:
employmentStatus) are reported in message, the same way as v1. Requests over the limits are rejected before any other check.
Statuses
SUBMITTED, FAILED and UNDER_REVIEW are final.
Webhook: contribution.submission_completed
Delivered through your registered webhook with the same HMAC signing and retries as every other event, once the submission reaches a final status:data.error: { "message": "..." } is included only when status is FAILED or UNDER_REVIEW. The message can be long (up to 4000 characters) when the clearing house reports many field errors. GET /v2/contributions/:transactionId returns the same fields as data.
Retrying safely
- No response / timeout / 5xx from
POST /v2/contributions— resend the same body with the sameIdempotency-Key. You’ll get the originaltransactionIdback; nothing is submitted twice. 400— nothing was stored. Fix the errors and send again (the same key is fine, since nothing was stored under it).FAILED— safe to fix and resubmit, with a newIdempotency-Key. Requests are re-checked just before sending, soFAILEDcan also mean something changed after you submitted (for example, an employee’s MVR status).UNDER_REVIEW— don’t resubmit. The money may already have gone through; our team will resolve it.
Limits
- 100 funds and 1000 members (across all funds) per request.
- Need more? Split the pay run into several requests, each with its own
Idempotency-Key.
