---
title: The Deliveries Log
description: "Every outbound call a Custom API integration makes is written down before it goes out, whether it succeeds, fails or never gets picked up. The **Deliveries** tab is that log: what was sent, what came back, what went wrong, and…"
url: "https://rackhelm.com/documentation/custom-api-deliveries/"
updated: "2026-09-24"
category: "Integrations: Custom API"
---

# The Deliveries Log

Every outbound call a Custom API integration makes is written down before it goes out, whether it succeeds, fails or never gets picked up. The **Deliveries** tab is that log: what was sent, what came back, what went wrong, and the one button that sends it again. This is the screen to open when a customer says their service never appeared.

## Where it is

Deliveries is the last tab of the integration editor. The older address `/admin/custom-apis/{id}/deliveries` still works and forwards onto the tab, carrying any filters with it, so old bookmarks and links from elsewhere in the admin still land correctly.

![The Deliveries tab showing status filter chips with counts above a table of outbound calls](/wp-content/uploads/rackhelm-docs/integrations-customapi-deliveries-tab.png)
Status chips carry live counts; the dropdown on the right filters by event.

## Reading the log

The chips across the top filter by status and show how many rows each holds. The dropdown on the card header filters by event. Both survive a page turn, and paginating brings you back to this tab rather than dumping you on Overview. Thirty rows are shown per page.

| Column | What it tells you |
| --- | --- |
| **When** | When the row was created — that is, when the call was queued, not when it completed. |
| **Event** | The lifecycle event, plus the action key for an action. |
| **Service** | The order and product, linking through to the package. |
| **Status** | The delivery status, with the response class underneath (`2xx`, `4xx`…). The exact upstream code is not shown here. |
| **Attempt** | How many times this particular record has been sent. |
| **Took** | Round-trip time in milliseconds. |

### The statuses

| Status | Meaning |
| --- | --- |
| `pending` | Queued, nothing has picked it up yet. |
| `sending` | In flight right now. A row stuck here for more than 15 minutes is treated as a worker that died, and may be claimed again. |
| `success` | The far end accepted the call, by your endpoint's own success rules. |
| `failed` | The call was made and not accepted, or could not be made at all. Terminal. |
| `cancelled` | Abandoned before sending — the integration was switched off, the product no longer points at it, or no active endpoint exists for the event any more. |
| `retrying` | Part of the status vocabulary, but nothing in RackHelm sets it today: this feature schedules no retries of its own. See below. |

### The details panel

Select **Details** on a row to open the request and response side by side:

- **Request** — method, full URL, headers and body, all redacted.
- **Response** — the stored reply body, again redacted.
- **Error** — the failure reason, when there is one.
- **Unresolved tokens** — tokens the template asked for and could not fill. The call went out with those gaps, so this is usually the answer when a provider complains about a missing field.
- **Reference** — the idempotency key, matching the `X-RackHelm-Idempotency-Key` header the far end received, plus who fired the call (a staff name, or "fired automatically").

> **Note:** Bodies and headers are masked twice — once by the sender before the row is written, and again before the page renders — so a credential or a generated password never appears on this screen. Bodies are also capped: a reply above the integration's **Max response stored** is refused rather than truncated and mis-read, and stored bodies are capped at 64 KB regardless.

## The retry model

This is the part that most often surprises people, so it is worth stating plainly.

**A Custom API integration has no retry ladder of its own.** There is no sweeper and no backoff schedule inside this feature. Each delivery row is a record of one occurrence of one event, and once it has been attempted it is terminal — it will never change again on its own. Retries live on the delivery row and nowhere else.

Where a retry does happen, it comes from the same machinery as every other integration:

| Event | How it retries |
| --- | --- |
| `provision` | Runs inside the standard provisioning pipeline. A failure marks the order item as failed, and the pending-provisioning scanner, the **Force Provision** button on the package, or **Retry Failed Provisioning** on the Pending Provisioning screen drives the next attempt. |
| `suspend`, `unsuspend`, `terminate` | Owned by the suspend/terminate jobs, exactly as for a control-panel driver. |
| Everything else | One attempt, recorded. You re-fire it from this screen. |

> **Warning:** A Custom API delivery failure is recorded on the delivery row and never written to the service's provisioning status. That is a safety property, not an oversight: if a failed *terminate* call reset the provisioning status back to pending, the scanner would re-claim the item and provision it again — resurrecting a service the customer had already cancelled, and re-billing it. Judge Custom API health from this log, not from the provisioning column.

## Replaying a call

The circular-arrow button on a finished row sends the same event again.

1. Open **Details** first and read the error — replaying a call that failed for a configuration reason will simply fail again.
2. Fix the cause: the endpoint template, the credentials, the allowlist, or something at the provider.
3. Select the replay button and confirm.

What happens next:

- The event is resolved against the integration *as it is now*, so your fix is used.
- A **new** delivery record is created rather than the old one being overwritten — the failure stays on screen as evidence.
- The call is queued, so the new row appears as soon as a worker picks it up.

Replay refuses, with a message, when:

| Message | Why |
| --- | --- |
| "That integration has been deleted…" | The integration is soft-deleted. Its history is kept, but nothing can be sent from it. |
| "This integration is switched off…" | **Active** is off on the Overview tab. |
| "This call has not finished yet…" | The row is still pending or sending. Replaying would send the same call twice. |
| "There is no active endpoint for that event any more…" | The endpoint was deleted or switched off since the failure. |
| "This record has no service or event to rebuild the call from…" | An incomplete row, typically from a much older build. |

## "Calls are queued but not going out"

If the oldest `pending` row has been waiting more than five minutes, a warning banner appears at the top of the tab. That almost always means nothing is draining the queue — check that the queue worker is running. Until it is, provisioning across the whole portal will look silently stuck rather than broken.

## Retention

This table gains a row per lifecycle event per service and stores whole request and response bodies, so it is pruned. The default window is **90 days**, controlled by the `custom_api_delivery_retention_days` setting, and the card header on this tab always shows the window currently in force. Pruning runs from the scheduler daily at 03:40. A value of zero or below is ignored and the 90-day default is used instead, so a stray zero cannot wipe the audit trail.

> **Tip:** These request and response pairs are the only forensic record of what was sent to a third party on a customer's behalf. If you operate under a regime that requires longer retention, raise the setting deliberately and plan for the table size.

## Troubleshooting

| Symptom | Cause and fix |
| --- | --- |
| No delivery rows at all for an order | The product is not attached to this integration, or the event has no endpoint. Check the product's integration setting and the **Endpoints** tab. |
| Rows sit on `pending` | The queue worker is not running. |
| A row is `cancelled` | The integration was off, the endpoint was removed, or the product no longer points at the integration when the event fired. |
| Failure reads "Response exceeded the N KB limit…" | Raise **Max response stored** on the **Connection** tab, or ask the provider for a leaner reply. The body was not decoded, so nothing was captured from it. |
| Failure mentions an invalid or unsafe URL | The rendered path resolved to a private or unroutable address, or a token blew the URL apart. Preview the endpoint on the **Test** tab. |
| Failure mentions no token, username or value stored | The auth type expects a credential the integration does not have. Retype it on the **Connection** tab. |
| The provider says they never received the call, but the row is `success` | Give them the value from **Reference** and the `X-RackHelm-Delivery` id from the details panel; those are the two identifiers that appear in their logs. |
| A replay appears to do nothing | It is queued, not synchronous. A new row appears when the worker picks it up — refresh the tab. |
| Old rows have disappeared | They were pruned. Check the retention window shown on the card header. |
