View Categories

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
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.