REFRACT Dispatch

REFRACT Dispatch API Reference

HTTP endpoint reference for batch dispatch management - get batch detail, validation results and audit chain, approve or reject, and cancel.

Batch dispatch is initiated via POST /api/render - there is no separate batch creation endpoint. Once a batch exists, the endpoints below manage its lifecycle.

All endpoints require a valid Entra ID JWT: a signed-in user's token with the user_impersonation scope, or an app-only token with the Render.Access permission. Submitting a batch requires the vaultDispatch feature on your licence. The endpoints that read a batch (Get Batch, Get Validation Results, and Get Audit Chain) also require the Lifecycle.Reader role in the token's roles claim, and return 403 with code ROLE_MISSING without it. See Access: the Lifecycle.Reader Role.

There is no endpoint that lists batches. Find a batch through its REFRACT_Activity item or REFRACT Lifecycle Search with ?batchId={batchId}.

Base URL

All paths are relative to your Dispatcher Function base URL, e.g. https://<your-dispatcher>.azurewebsites.net.


Submit a Batch

POST /api/render

Batch dispatch uses the standard render endpoint with dataSource as an array and features.vaultDispatch: true.

At most 1,000 records per batch

A batch holds at most 1,000 records, or fewer where your license sets a lower limit. A larger batch is refused before anything is stored: the response is an error-report PDF with status 400 and code BATCH_TOO_LARGE, stating the limit and the number of records received, and no batch, receipt or Activity item is created and nothing is sent. To send more documents, split the records into several smaller batches and submit each one.

Auth: Entra ID JWT

Request body:

{
  "templatePath": "templates/account-statement.rpkg",
  "options": {
    "correlationId": "batch-stmt-may-2026",
    "documentId": "BATCH-STMT-MAY-2026",
    "generatedBy": "system@contoso.com",
    "sourceSystem": "D365_FO",
    "documentIdTemplate": "STMT-{{accountNumber}}"
  },
  "dataSource": [
    {
      "accountNumber": "ACC-001",
      "accountName": "Contoso Ltd",
      "recipientEmail": "customer1@contoso.com",
      "statementPeriod": "May 2026",
      "balance": 850.00
    },
    {
      "accountNumber": "ACC-002",
      "accountName": "Fabrikam Inc",
      "recipientEmail": "customer2@fabrikam.com",
      "statementPeriod": "May 2026",
      "balance": 2100.00
    }
  ],
  "templateSettings": {
    "distribution": {
      "mode": "individual",
      "requireApproval": true,
      "fileNameTemplate": "Statement_{{accountNumber}}_{BATCHID_1}",
      "approversEmail": "ap-approver@contoso.com",
      "deliveryConfig": {
        "mode": "secure-email",
        "expiresInHours": 168
      }
    }
  },
  "features": {
    "vaultDispatch": true
  }
}

Response 202:

Response body is the batch intake receipt PDF (binary). The batch identifiers are in the response headers:

HeaderDescription
X-Refract-BatchIdUnique batch identifier. Use this with all /api/dispatch/batch/* endpoints.
X-Refract-CorrelationIdCorrelation ID for the batch activity record. Links to the REFRACT_Activity item in SharePoint.
X-Refract-DispatchReadinessVALID, WARNINGS, or INVALID - overall validation outcome across all records.

INVALID batches return 422

When X-Refract-DispatchReadiness is INVALID, the status code is 422 Unprocessable Entity. The intake receipt PDF's Outcome says the batch was refused, with how many records are invalid, their rows, and each kind of error, and its Validation Results describe each record's issues. Nothing is queued, rendered or sent. Correct the records and resubmit.


Get Batch

GET /api/dispatch/batch/{batchId}

Returns the full detail of a single batch: its processing counters, decision history, audit chain verification, and delivery results. Each record's validation result is not included; page through them with Get Validation Results.

Auth: Entra ID JWT with the Lifecycle.Reader role

Response 200 (abridged):

{
  "batchId": "a3f1c2d4-8e9b-4f0a-b2c3-1d4e5f6a7b8c",
  "state": "completed",
  "dispatchReadiness": "VALID",
  "title": "May 2026 Account Statements",
  "templatePath": "templates/account-statement.rpkg",
  "documentCount": 150,
  "readyCount": 148,
  "failedCount": 0,
  "warningCount": 2,
  "processedCount": 150,
  "submittedBy": "system@contoso.com",
  "approvedBy": "ap-approver@contoso.com",
  "createdAt": "2026-06-09T10:00:00.000Z",
  "decidedAt": "2026-06-09T10:15:00.000Z",
  "queuedAt": "2026-06-09T10:15:01.000Z",
  "completedAt": "2026-06-09T10:42:00.000Z",
  "decisionComments": "Validated - proceed",
  "sendsToRecipients": true,
  "auditChain": { "status": "verified", "events": 312, "brokenAt": [], "gaps": [] },
  "deliveryResults": { "sent": 146, "failed": 1, "pending": 3, "expected": 150, "lastReportedAt": "2026-06-09T10:44:12.000Z" }
}
FieldDescription
auditChainWhether the batch's whole audit chain verifies: status is verified or broken (with the event count and the positions of any broken lines or gaps), or none or unreadable alone.
deliveryResultsRecipients whose invitation email was sent (accepted by the email service) or failed, and pending (expected minus sent and failed). pending and expected are null for a batch whose recipient counts were not recorded. The whole field is null when the batch queued no deliveries or the chain could not be read. See Delivery Results.

Get Validation Results

GET /api/dispatch/batch/{batchId}/validation-results?offset=0&limit=50&status=warning

Returns each record's validation result a page at a time, in record order. offset defaults to 0; limit defaults to 50 and is at most 200; status (valid, warning or invalid) is optional and returns only the records with that status. total is the number of matching records, and counts covers the whole batch. Results that cannot be read are reported in validationResultsUnavailable instead of being shown as none.

Auth: Entra ID JWT with the Lifecycle.Reader role

Response 200:

{
  "batchId": "a3f1c2d4-8e9b-4f0a-b2c3-1d4e5f6a7b8c",
  "status": "warning",
  "total": 2,
  "offset": 0,
  "limit": 50,
  "validationResults": [
    { "index": 17, "status": "warning", "fields": { "accountNumber": "ACC-1018" }, "issues": [{ "field": "balance", "code": "NON_NUMERIC_SUMMARY_FIELD", "message": "batchSummary field \"balance\" has no numeric values on record 17", "severity": "warning" }] }
  ],
  "counts": { "total": 150, "valid": 148, "warning": 2, "invalid": 0 }
}

Get Audit Chain

GET /api/dispatch/batch/{batchId}/audit-chain?offset=0&limit=50&kind=delivery

Returns the batch's audit chain a page of entries at a time. offset defaults to 0; limit defaults to 50 and is at most 200. Every read verifies the whole chain, so status, events, brokenAt, and gaps always describe the whole chain, not the page, and each entry's broken flag reflects its place in the whole chain.

kind is optional. With kind=delivery, only the delivery result events (BATCH_DOCUMENT_DELIVERY_SENT and BATCH_DOCUMENT_DELIVERY_FAILED) are paged, and total is their count. Any other value of kind is refused with 400. A batch with no chain, or one that cannot be read, returns only status (none or unreadable).

Auth: Entra ID JWT with the Lifecycle.Reader role

Response 200 (abridged):

{
  "batchId": "a3f1c2d4-8e9b-4f0a-b2c3-1d4e5f6a7b8c",
  "status": "verified",
  "events": 312,
  "brokenAt": [],
  "gaps": [],
  "offset": 0,
  "limit": 50,
  "kind": "delivery",
  "total": 150,
  "entries": [
    {
      "seq": 207,
      "event": "BATCH_DOCUMENT_DELIVERY_FAILED",
      "timestamp": "2026-06-09T10:51:02.000Z",
      "data": {
        "batchId": "a3f1c2d4-8e9b-4f0a-b2c3-1d4e5f6a7b8c",
        "index": 41,
        "documentCorrelationId": "c7d2e5f8-1a3b-4c6d-9e0f-2a4b6c8d0e1f",
        "recipientHash": "4f9c0a7e2b1d...",
        "reportedBy": "refract-notification",
        "reportedAt": "2026-06-09T10:50:40.000Z",
        "stage": "send",
        "errorCode": "acs_http_400"
      },
      "broken": false
    }
  ]
}

index is the record's zero-based position in the batch (REFRACT Lifecycle shows it as Record 42). recipientHash is a keyed hash of the recipient's address, never the address. reportedBy is refract-notification, customer-acs, or processor. A sent event that follows a failure for the same recipient carries followsFailure: true. See Delivery Results.


Decide (Approve / Reject / Request Changes)

POST /api/dispatch/batch/{batchId}/decide

Records the approver's decision. Only a batch in pending-approval state can be decided; any other state returns 409 ("Batch cannot be decided in state: ...").

Auth: Entra ID JWT of a signed-in approver. An app-only token gets 403 with code APP_CANNOT_DECIDE. When the batch has an approver list, a sign-in without an email address is refused with 403 ("Your sign-in did not carry an email address, so it cannot be matched to the approvers of this batch.").

Request body:

{
  "decision": "approve",
  "comments": "Validated against AP system - proceed."
}
FieldTypeRequiredDescription
decisionapprove | reject | request-changesYesThe approver's decision. approve enqueues the batch for processing. reject ends the batch with no PDFs produced. request-changes returns the batch to changes-requested state with the comments.
commentsstringNoFree-text comments visible in the batch record and activity timeline. At most 1,000 characters, not counting leading and trailing spaces: longer comments are refused with 400 (DECISION_COMMENTS_TOO_LONG, maxLength: 1000), stating the limit and the length received, and the batch is not changed. The batch's audit chain records a SHA-256 hash of the comments and their length, not the text. See Decision comments.

Response 200:

{
  "batchId": "a3f1c2d4-8e9b-4f0a-b2c3-1d4e5f6a7b8c",
  "state": "approved",
  "decidedAt": "2026-06-09T10:15:00.000Z"
}

The response reports approved; the batch is already queued

The state in this response is approved, but on an approve decision the batch is enqueued for processing in the same call - by the time you receive this response, the batch's stored state is already queued. Poll GET /api/dispatch/batch/{batchId} if you need to observe that transition.

Auto-approve

When distribution.requireApproval: false in the template settings, the batch is auto-approved at intake time and enters queued state immediately - no /decide call is required.


Cancel Batch

POST /api/dispatch/batch/{batchId}/cancel

Cancels a batch that has not yet started processing. Cancellable states: pending-approval, changes-requested.

Auth: Entra ID JWT

Response 200:

{ "batchId": "a3f1c2d4-...", "state": "cancelled" }

Tracking Individual Records

Each record in the batch gets its own correlationId (generated by the Processor). To track individual records after processing:

  • Query the REFRACT_Activity SharePoint list filtered by ReferenceID = {batchId} - all child activity items link back to the batch via ReferenceID.
  • Use REFRACT Lifecycle Search with ?batchId={batchId} to view all documents from the batch.
  • Query delivery sessions: GET /api/delivery-sessions?correlationId={itemCorrelationId}.
  • For the batch's email results per recipient, read deliveryResults on Get Batch or page the delivery events with Get Audit Chain and kind=delivery.

REFRACT Dispatch Overview

Batch lifecycle states, distribution template settings, per-record data requirements, and the full approval flow.

On this page