Instructions for collection modules
Instructions for collection modules
Instructions are first-class platform entities that let you schedule a collection module function to execute at a specific date and time in the future. They are the preferred scheduling mechanism on the platform — use them for any deferred collections work: scheduled retries outside the standard billing cadence, time-bounded payment-method follow-ups, end-of-term collection cleanup, deferred configuration changes.
Collection-module instructions run on the same platform scheduler as product-module instructions — same cron cadence, statuses, idempotency, and permissions — with a few deliberate differences in how they are created and what the function receives. This guide covers the collection-module implementation; the differences from product modules are called out throughout.
How they work
An instruction stores:
- The collection module it targets (
module_id) - Optionally, a policy for context
- The function name to invoke
- A module payload — free-form data passed to the function at execution
- An execution datetime — when to run, must land on a 30-minute boundary (
:00or:30)
The platform runs a cron job every 30 minutes that picks up every pending instruction whose execution_datetime has passed. Each is dispatched via a shared FIFO queue, and the named function is invoked on the collection module's deployed code.
Lifecycle
pending → queued → executed → successful
↘ failed
↘ cancelled
| Status | Meaning |
|---|---|
pending | Created. Waiting for the next cron run (every 30 minutes) to pick it up. |
queued | Cron has dispatched it; execution will follow within seconds. |
executed | Function has started running. |
successful | Function returned without throwing. Terminal. |
failed | Function threw, the target was unavailable, OR a platform sweep marked it failed (see Failure modes). Terminal. |
cancelled | Manually cancelled before execution. Terminal. |
Execution ordering
- Instructions created with a
policy_idserialize per policy (FIFO message group = the policy). - Instructions created without a
policy_idserialize per collection module (FIFO message group = the module ID) — so policy-less instructions for the same module execute one at a time, inexecution_datetimeorder. - Instructions in different groups execute in parallel, up to the platform's concurrency limit (default 50).
Creating an instruction
Collection-module instructions are created via the API only. Unlike product modules, collection module code cannot return add_instruction (or any instruction action) from its hooks — the collection-module action set is limited to payment scheduling actions (schedule_payment, reschedule_payment, unschedule_payment).
POST /v1/insurance/instructions
Content-Type: application/json
Authorization: Basic <api_key>:
{
"module_type": "collection_module",
"module_id": "11111111-2222-3333-4444-555555555555",
"function_name": "retry_failed_collection",
"module": {
"collection_attempt": 2,
"original_failure_reason": "insufficient_funds"
},
"execution_datetime": "2026-08-01T14:00:00.000Z",
"description": "Retry the failed collection after the grace window",
"manually_executable": true
}Request shape — differences from product modules:
| Field | Collection module behavior |
|---|---|
module_type | "collection_module" |
module_id | Required. The collection module ID. It is authoritative — validated for ownership at creation and used directly at dispatch. |
policy_id | Optional. Context only — validated if supplied, and used for execution ordering (see above). The engine does not need it to dispatch. |
claim_id | Not supported. Rejected with a 400 — collection-module instructions cannot be claim-scoped. |
The payment method is deliberately not part of the instruction: it can change between scheduling and execution, so resolve it inside the function at execution time.
Required validation (same as product modules):
execution_datetimemust be at least 15 minutes in the futureexecution_datetimemust land on a 30-minute boundary — minutes are00or30, seconds/milliseconds zero (e.g.14:00:00.000Zor14:30:00.000Z, never14:15:00.000Z)
Optional fields description, hidden, and manually_executable behave exactly as for product modules (defaults null, false, false).
Response: 201 Created with the new instruction, OR 200 OK with the existing instruction if an exact duplicate already exists (see Idempotency).
The instruction function
At execution time, the platform invokes the named function on the collection module's deployed code with a single argument containing two keys only:
async function retry_failed_collection({
instruction_data, // the `module` payload supplied at creation
instruction, // the full PlatformInstruction row
}) {
// Resolve current state at execution time — the function does NOT
// receive policy, policyholder, or claim arguments.
const attempt = instruction_data.collection_attempt;
// ... perform the deferred collections work
}Key differences from product-module instruction functions:
- No
policy,policyholder, orclaimarguments. The function receives onlyinstruction_dataandinstruction. If you need policy or payment-method state, load it inside the function so you always act on current data. - The return value is ignored. Collection-module instruction functions are fire-and-forget — there is no action-array processing. Perform your work through the collection module's normal capabilities inside the function body.
instruction_dataandinstruction.modulecarry the same value —instruction_datais provided as a separate argument for convenience.
Idempotency
Identical to product modules: exact duplicates are deduplicated on (organization, environment, module_id, policy_id, function_name, execution_datetime, module) while pending, with the existing row returned as 200. JSON key order in module is normalised. Two policy-less instructions with otherwise identical fields are treated as exact duplicates. Vary the module payload to force a distinct row at the same slot.
Cancelling and triggering
- Cancel:
POST /v1/insurance/instructions/{instruction_id}/cancel— onlypendinginstructions can be cancelled; cancelled rows don't block re-creation. Same as product modules. - Manual trigger:
POST /v1/insurance/instructions/{instruction_id}/trigger— available for instructions created withmanually_executable: true; fires synchronously before the scheduled datetime. Same as product modules. - Bulk cancel is not available. The
bulk_cancel_instructionsaction is a product-module-code action and there is no HTTP equivalent — cancel collection-module instructions one at a time via the/cancelendpoint.
Failure modes
The generic failure reasons apply exactly as for product modules — stale (more than 24 hours overdue at pickup), stuck_in_queued and stuck_in_executed (sweep timeouts after 30 minutes), and function_not_found (the named function is not defined in the deployed collection module code).
Collection-module specifics:
| Scenario | Result |
|---|---|
| The function threw an exception | failed with failure_reason: null. Details are in the collection module logs. |
| The named function doesn't exist in the deployed code | failed with failure_reason: function_not_found. |
| The target is unavailable — collection module deleted, no live definition, or no deployed code | failed with failure_reason: null. Deliberately terminal (retrying would never succeed). |
| Transient infrastructure error (Lambda/transport failure, 5xx, timeout) | Reverted to queued and retried via SQS redelivery; only exhausted redeliveries end in stuck_in_queued. |
Note the observability difference: collection-module instruction runs do not produce a product-module run-log row (product_module_code_run_id stays null). To investigate a failure, look in the collection module logs.
failed instructions are not retried automatically. To re-execute, create a new instruction.
Listing instructions
The same generic list/count surface applies — scope to collection modules with module_type:
GET /v1/insurance/instructions?module_type=collection_module
GET /v1/insurance/instructions?module_type=collection_module&module_id={collection_module_id}&status=pending,failed
HEAD /v1/insurance/instructions?module_type=collection_module # returns just the total-count headerAll standard filters, pagination, single fetch (GET /instructions/{instruction_id}), and the dashboard aggregate endpoints (/instructions-stats, /instructions-status-counts) work for collection-module instructions — see the product-module instructions guide for the full reference.
Permissions
Identical to product-module instructions: instructions:read, instructions:read_hidden, instructions:create, instructions:trigger, instructions:cancel.
Operational guidance
- Resolve state at execution time. Because the function receives no policy or payment-method arguments, and because payment methods can change between scheduling and firing, always load current state inside the function.
- Keep the
modulepayload small and schema-validate it inside the function — the platform doesn't validate its shape. - Spread large batches across slots. As with product modules, avoid concentrating thousands of instructions on a single 30-minute boundary.
- Defensive re-creation is safe — exact duplicates are idempotent no-ops.
Related guides
- Instructions (product modules) — the product-module implementation and full API reference
- Collection modules — the broader collections system
Updated about 4 hours ago