Skip to content

Read one delivery's attempts

GET
/v1/webhooks/{webhookId}/deliveries/{deliveryId}/attempts
curl --request GET \
--url https://api.roundrobinbot.eu/v1/webhooks/example/deliveries/example/attempts \
--header 'Authorization: Bearer <token>'

The whole retry ladder of one delivery, oldest attempt first, each with the exact body and headers that attempt sent. The body is what was posted at the time, byte for byte, which is what a signature verifies over. It is not the rotation as it stands now: the payload carries state, so an event sent yesterday describes yesterday. Always one page, because a delivery has as many attempts as the ladder has rungs, so page and pageSize are not accepted here. No ETag: the rows are append-only and nothing here carries a version.

webhookId
required
string
deliveryId
required
string

OK

Media typeapplication/json

The envelope every /v1 collection comes back in.

object
data
Array<object>

One attempt with the exact bytes we sent it with.

object
requestBody

The exact bytes we POSTed.

string
requestHeaders

The headers we sent, including Round-Robin-Signature and Round-Robin-Timestamp, so the HMAC over the body above can be reproduced.

Array<object>

One header we sent, spelled exactly as it went out.

object
name
string
value
string
responseBodySnippet

A bounded snippet of what your server answered with, on an attempt that got an answer. Absent on a row where nothing was sent.

null | string
responseBodyTruncated

Whether your real answer was longer than the snippet above carries.

boolean
retryAfterSecondsRequested

What your Retry-After header asked for, in seconds, on a 429 or 503 answer. Absent otherwise. Not necessarily what the next attempt waited: the ladder clamps this, so a receiver can ask us to wait longer than it otherwise would, never to hurry it.

null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
deliveryId

The id your receiver deduplicates on. Minted once per event and identical across every attempt, so two calls carrying the same id are the same event and the second is safe to ignore.

string
attemptNumber

1 for the first send, incrementing over the retry ladder.

integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
eventType

The event type, as it appears in the payload: duty.changed and the rest.

string
rotationId

The rotation the event happened on.

string
outcome

How the attempt ended.

string
statusCode

The status code your server answered with, absent when nothing answered. That absence is the whole difference between failed and no_response.

null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
durationMs

How long the attempt took, from the request leaving to the answer or the failure that ended it. Absent when no request was made.

null | integer | string format: int64
/^-?(?:0|[1-9]\d*)$/
error

Why the attempt ended when no status code explains it, from a closed snake_case vocabulary: timeout, connection, secure_connection, name_resolution, redirect_not_followed, private_address and the rest. Never the receiver’s own answer, which the attempts route reports separately as a bounded response snippet.

null | string
at

When the attempt was made.

string format: date-time
round

Which round of the retry ladder this attempt belongs to, counting from 1: the first send and its in-process retries are round 1, then each scheduled redelivery step is its own round.

null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
totalRounds

How many rounds the retry ladder has today, so you never have to hard-code its shape.

integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
nextAttemptDueAt

When the next attempt is due, if there is one. Absent when this attempt was delivered, when nothing more will be tried, or on a row recorded before this field existed.

null | string format: date-time
page
integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
pageSize
integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
totalItems
integer | string format: int64
/^-?(?:0|[1-9]\d*)$/
totalPages
integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
Examplegenerated
{
"data": [
{
"requestBody": "example",
"requestHeaders": [
{
"name": "example",
"value": "example"
}
],
"responseBodySnippet": "example",
"responseBodyTruncated": true,
"retryAfterSecondsRequested": 1,
"deliveryId": "example",
"attemptNumber": 1,
"eventType": "example",
"rotationId": "example",
"outcome": "example",
"statusCode": 1,
"durationMs": 1,
"error": "example",
"at": "2026-04-15T12:00:00Z",
"round": 1,
"totalRounds": 1,
"nextAttemptDueAt": "2026-04-15T12:00:00Z"
}
],
"page": 1,
"pageSize": 1,
"totalItems": 1,
"totalPages": 1
}

Bad Request

Media typeapplication/json
object
type
null | string
title
null | string
status
null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
detail
null | string
instance
null | string
Examplegenerated
{
"type": "example",
"title": "example",
"status": 1,
"detail": "example",
"instance": "example"
}

Unauthorized

Media typeapplication/json
object
type
null | string
title
null | string
status
null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
detail
null | string
instance
null | string
Examplegenerated
{
"type": "example",
"title": "example",
"status": 1,
"detail": "example",
"instance": "example"
}

Forbidden

Media typeapplication/json
object
type
null | string
title
null | string
status
null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
detail
null | string
instance
null | string
Examplegenerated
{
"type": "example",
"title": "example",
"status": 1,
"detail": "example",
"instance": "example"
}

Not Found

Media typeapplication/json
object
type
null | string
title
null | string
status
null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
detail
null | string
instance
null | string
Examplegenerated
{
"type": "example",
"title": "example",
"status": 1,
"detail": "example",
"instance": "example"
}