Topolo Docs Topolo Platform
Open workspace

Retry a submission’s delivery processing

Review reset, new-channel and all-due-row execution effects before retrying delivery processing.

Outcome

Review reset, new-channel and all-due-row execution effects before retrying delivery processing.

POST /api/forms/{formId}/deliveries/retry

Before you start

Use the original authorized organization and canonical Forms workspace discovered through supported Auth resources. Bind the workspace separately; resolve form and submission ids through authorized existing Forms reads. Never execute documentation fixtures against real resources.

Both are confirmed mutations requiring application workspace:write and a user-bearer workspace read_write grant. API keys retain the exact Auth-verified workspace resource binding. Approval must cover actual answers/subjects, destinations/recipients, privacy decisions, billing and potentially external effects, not just a technical POST.

All examples are source-derived synthetic UNRUN. No form/subject read, submission, retry, CRM/Consent/Notify/Nexus, meter, provider/model or key operation was executed. Current source/declaration matching is separate from serving-runtime and provider proof.

Request and effects

Action: app_topolo_forms.submissions.deliveries.retry. Validate current input, plan the exact intended mutation, bind the canonical workspace separately and confirm only the approved operation.

Input requires discovered formId andsubmissionId. Optional channels is a nonempty subset of confirmation,notification,crm,consent,outbound; omitted means allfive. After scoped form lookup, source validates a JSON body up to4000bytes, trims submissionId, verifies the submission in the sameform/org/workspace, and rejects missing submission404 submission_not_found.

It loads current settings and INSERTORIGNOREenqueues requested currentlyenabled channels that lack a row. Existing rows keep their prior protected snapshots. New rows can send an old response to a newly enabled destination using current form/settings; this is an intentional new external effect needing review, not merely retrying previously approved settings.

For requested channels, failed/skipped rows reset to pending,attempts0,nextAttemptAtnow with leases cleared. It does not clear prior lastError/detail at that reset. Delivered/accepted rows are not reset. returned retried is the number of reset rows, not created rows, attempts, successes, respondents or successful sends.

Critical scope boundary: channels filters enqueue/reset, but the subsequent runSubmissionDeliveries iterates every ledger row for that submission, not only requested channels. Other existing due pending/failed rows or expired in_progress leases can therefore run too. A caller approving only one channel must not assume channels isolates execution. Review/approve all potentially due rows or obtain owner correction before invoking.

Each run uses a database conditionalclaim, incrementsattempts and assigns a two-minute lease/token. Only due pending/failed rows or expired in_progress rows can claim; unexpired lease, delivered,accepted,skippedwithoutreset or future retry rows do not claim. Competing workers are coordinated by conditional storage, not a global external exactly-once guarantee.

The runner first requires current scoped form/submission context even for an existing snapshot, then reads that snapshot or persists a legacy snapshot fallback. It executes channel-specific ports and stores accepted/delivered/skipped/failed with conditional lease-token settlement. Context failure is recorded as failed delivery_context_unavailable with retry scheduling where possible. A missing context can become skipped submission_not_found.

Retryable failures schedule attempts up to6automaticattempts with backoff1minute,5minutes,30minutes,2hours,12hours. Manual reset returns attempts to0 and can restart that budget. No extra schedule is installed by this action; invocation of the existing cron and its loaded runtime are separate.

Notify channels, Consent updates and CRM/outbound Nexus handoffs can be external operations. CRM/outbound storedaccepted means Nexus handoff, not downstream completion. Other channel storeddelivered reflects the adapter result; it is not independently verified emailreceipt/legalconsent/businesssuccess. Detailed progress may be retained across retries.

The endpoint waits for its runner and returns HTTP200 {retried,deliveries:[...all submission rows...]}. Rows use the same11field projection as the ledger read. No success:true or new submissionid envelope is required. HTTP200 with retried0 may mean noeligiblefailed/skippedrows, accepted/deliveredterminalrows, future/leased rows, or newlyenqueuedwork; inspect actual records rather than interpreting0as no effects.

Reset, enqueue, key/snapshot work, external effects and final ledger read are separate. If response or settlement is lost, preserve uncertainty; never claim that a delivered/accepted guard proves no duplicate external effect after an interrupted pre-settlement attempt. Reconcile actual ledger and authorized downstream evidence before deciding on any further retry. No automatic inverse is published.

Worked examples

A previously accepted handoff is not reset

Complete source-derived synthetic UNRUN HTTP200. Original authorized form/submission exists, all settingsdisabled, this existing outbound row is accepted, no other due rows. No new row/reset/attempt and no provider call in the imagined case; deliveryid is fictional.

Input:

{
  "formId": "forms_docs_fixture",
  "submissionId": "formsub_docs_fixture",
  "channels": [
    "outbound"
  ]
}

Response:

{
  "retried": 0,
  "deliveries": [
    {
      "id": "formdelivery_docs_fixture",
      "submissionId": "formsub_docs_fixture",
      "channel": "outbound",
      "status": "accepted",
      "attempts": 1,
      "lastError": null,
      "detail": {
        "nexusDeliveryIds": [
          "delivery_docs_fixture"
        ]
      },
      "nextAttemptAt": null,
      "deliveredAt": null,
      "createdAt": "2026-10-20T00:00:00.000Z",
      "updatedAt": "2026-10-20T00:00:01.000Z"
    }
  ]
}

Retain only actual approved scope/response evidence; fictional ids/tokens and this example authorize no live submission, retry, private read or external effect.

No channel rows and nothing enabled

Complete source-derived synthetic UNRUN HTTP200. Valid fictional existing submission, allcurrentchannelsdisabled, noexistingledgerrows. Empty is a successful scoped read, not a swallowed database/protection failure.

Input:

{
  "formId": "forms_docs_fixture",
  "submissionId": "formsub_docs_fixture"
}

Response:

{
  "retried": 0,
  "deliveries": []
}

Retain only actual approved scope/response evidence; fictional ids/tokens and this example authorize no live submission, retry, private read or external effect.

A reset row settles skipped from its disabled snapshot

Complete source-derived synthetic UNRUN HTTP200. One existing failed confirmation row has a readable snapshot with confirmationdisabled; no otherrows. Reset→conditionalclaim increments to1→channel_disabled skips without Notify; retaineddetail is empty. Fixed illustrative timestamp; no real attempt ran.

Input:

{
  "formId": "forms_docs_fixture",
  "submissionId": "formsub_docs_fixture",
  "channels": [
    "confirmation"
  ]
}

Response:

{
  "retried": 1,
  "deliveries": [
    {
      "id": "formdelivery_docs_fixture",
      "submissionId": "formsub_docs_fixture",
      "channel": "confirmation",
      "status": "skipped",
      "attempts": 1,
      "lastError": "channel_disabled",
      "detail": {},
      "nextAttemptAt": null,
      "deliveredAt": null,
      "createdAt": "2026-10-20T00:00:00.000Z",
      "updatedAt": "2026-10-21T00:00:00.000Z"
    }
  ]
}

Retain only actual approved scope/response evidence; fictional ids/tokens and this example authorize no live submission, retry, private read or external effect.

Submission missing in this form and workspace

Complete source-derived synthetic UNRUN HTTP404. Form exists, auth/scope/body pass, selectedsubmissionqueryreturnsnorow beforeenqueue/reset/run.

Input:

{
  "formId": "forms_docs_fixture",
  "submissionId": "formsub_absent_docs_fixture"
}

Response:

{
  "error": "submission_not_found"
}

Retain only actual approved scope/response evidence; fictional ids/tokens and this example authorize no live submission, retry, private read or external effect.

Verify and recover

  1. Retain exact actual response ids,request id,original organization/workspace,approved input and timestamps. For intake use separately authorized submissions.list to match submission.id; the201 body does not echo form/workspace or downstream results.

  2. Read the actual delivery ledger only when authorized. Interpret status,attempts,detail,Nexusids and per-channel snapshot effects separately; do not infer provider completion or all-channel success from HTTP200/201 or retriedcount.

  3. After uncertain storage/lease/adapter/transport effects, reconcile before any repeat. Never use a fresh submission,delete/erase,settingschange or another tenant as recovery. A documentation example grants no action authority.

No safe automatic inverse is published. Preserve uncertain effects and reconcile the actual authorized records/provider outcomes with their owner before any separately reviewed retry. Broad form deletion/organization erasure cannot undo downstream sends or decisions.

Refusals

403 / forbidden: Preserve original identity/scope and actual denial; obtain legitimate write grant from its owner.

404 / not_found: Reconcile discovered form and original authorized workspace; submission intake also requires published form.

Concrete collector/retry errors are documented above. Shared auth/key/database failures retain their actual codes and request evidence; no invented universal carrier. Invalid local schema input is distinct from an actual handler refusal.

Change log

  • 2026-10-21: Added source-qualified validation and mutation order, precise response examples, non-atomic/uncertain effects, delivery-scope and provider/billing distinctions, with approved verification and no automatic inverse. Examples remain synthetic and unrun.