Skip to main content
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/contributions with your Idempotency-Key header.
  • 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 usi is an active fund. If anything fails you get a 400 listing every error, and nothing is stored.
  • 202 Accepted — you get back a transactionId. This is the one SuperChoice transaction ID used everywhere afterwards: payments, payment status endpoints, and payment.status_changed webhooks.
  • 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_completed webhook fires, or poll GET /v2/contributions/:transactionId.
  • After SUBMITTED — everything else (payment status, payment.status_changed webhooks, fund responses) works exactly as it does for v1 transactions.

Idempotency-Key

The Idempotency-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

A 400 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:
Field-format errors (e.g. a missing amount or an invalid 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 same Idempotency-Key. You’ll get the original transactionId back; 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 new Idempotency-Key. Requests are re-checked just before sending, so FAILED can 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.
Last modified on November 10, 2026