Submit scheduled payments

Submit scheduled payments through your collection module when you use an API payment provider.


Choose the submission path

How a due payment reaches a provider depends on the provider type.

API payment providerFile-based debit provider
Configbatching.enabled: true + submitPaymentsFunctionbatching.enabled: false
Who submitsYour module's hookThe platform batcher
BatchingYou decide, inside your hookThe platform handles it
Code you writeA submitPayments hookNone

For file-based debit providers, you write no submission code. The platform batcher submits those payments for you.


The submitPayments hook

For API providers, the platform calls your submission hook with the whole batch of your module's due payments in one call. You submit them to your provider and return a result for each one.

submitPayments is an example name. The platform calls whatever function you name in submitPaymentsFunction, so your exported function and that config value must match.

export const submitPayments = async ({ payments, organization, environment }) => {
  const results = [];

  for (const p of payments) {
    try {
      // `provider` is your payment provider's client.
      const charge = await provider.charge({
        amount: p.amount,
        currency: p.currency,
        idempotencyKey: p.payment_id,
      });

      results.push({
        payment_id: p.payment_id,
        status: 'submitted',
        provider_reference: charge.id,
      });
    } catch (error) {
      results.push({
        payment_id: p.payment_id,
        status: 'failed',
        failure_reason: error instanceof Error ? error.message : 'provider_submission_failed',
      });
    }
  }

  return { results };
};

This example submits one payment at a time. You can submit in parallel, as shown in the scheduled payments example, as long as you stay within your provider's rate limits.


Parameters

The hook receives { payments, organization, environment }. Each entry in payments has these fields:

FieldDescription
payment_idThe payment's id. Use it as the provider idempotency key.
policy_idThe policy the payment belongs to.
amountThe payment's resolved amount, in the smallest currency unit. Corresponds to the schedule's expected_amount. Note the field is amount here, not expected_amount.
currencyThe ISO currency code.
premium_typeThe kind of payment, for example recurring. See the schedule action's premium_type for the full list of values.
billing_period_startThe first day of the period this payment covers.
billing_period_endThe last day of the period this payment covers.
policyholderThe policyholder object.
policyThe policy object.

organization is the organization the payments belong to. environment is sandbox or production.


Return shape

Return { results: [...] }, with one entry per payment.

status reports submission, not settlement. Return submitted when your provider accepts the request, and failed when the submission itself fails. A payment you report as submitted stays in the submitted state. The platform does not yet resolve it to successful or failed automatically; settlement confirmation is not available for API providers.

FieldRequiredDescription
payment_idYesThe payment_id from the matching input payment.
statusYesEither "submitted" or "failed".
provider_referenceWhen submittedThe provider's id for the payment, for example a charge or transaction id. Stored on the payment for your reconciliation.
failure_reasonWhen failedA short reason the submission failed.

The platform validates your return value before it applies any results.

A mixed batch can return both submitted and failed payments:

{
  "results": [
    {
      "payment_id": "pay_123",
      "status": "submitted",
      "provider_reference": "ch_456"
    },
    {
      "payment_id": "pay_789",
      "status": "failed",
      "failure_reason": "card_declined"
    }
  ]
}

Idempotency

Always use payment_id as the idempotency key with your provider, as shown above. The platform may call your hook again if a batch is redelivered, and without an idempotency key a retried call charges the policyholder twice.


Next step

Review scheduled payment events and troubleshooting to monitor submissions and diagnose common issues.