API reference
Messages
Read the messages your devices sent, follow each delivery attempt, and retry deliveries from the dead letter queue.
Every message a device sends is stored and forwarded to its customer's webhook endpoints. A delivery that fails three attempts lands in the dead letter queue (DLQ) for 7 days, where Owners and Admins can retry it. Resellers see every message's delivery status but never its payload, which only the owning customer can read. Cellular connectivity is coming soon: today every message's source is satellite, so the cellular values and the not_forwarded status don't occur yet.
Endpoints
- get
/api/json/inbound_messagesList messages - get
/api/json/inbound_messages/{id}Get a message - patch
/api/json/inbound_messages/{id}/retryRetry a message - post
/api/json/inbound_messages/bulk/retryRetry messages in bulk - get
/api/json/delivery_attemptsList delivery attempts - get
/api/json/delivery_attempts/{id}Get a delivery attempt
The message object
One message from a device and the state of its deliveries.
Attributes
iduuidRequiredUnique identifier.
attempt_countintegerRequiredDelivery attempts so far, over all endpoints.
cancelled_endpoint_idsarray of uuidRequiredEndpoints disabled or deleted before they accepted the message.
customer_iduuid· nullableThe device's customer when the message arrived (snapshot); null: none.
dead_lettered_attimestamp· nullableWhen it moved to the DLQ.
dead_lettered_endpoint_idsarray of uuidRequiredEndpoints that failed all their attempts.
delivered_attimestamp· nullableWhen the last endpoint accepted it (status
delivered).delivered_endpoint_idsarray of uuidRequiredEndpoints that accepted the message (2xx).
delivery_statusenumRequiredpending | retrying | delivered | dead_lettered | no_endpoint | not_forwarded.
One of
pendingretryingdelivereddead_letteredno_endpointnot_forwardeddevice_iduuidRequireddlqboolean· nullableIn the dead-letter queue (
filter[dlq]=true).endpoint_idsarray of uuidRequiredThe customer's enabled webhook endpoints when the message arrived.
expires_attimestamp· nullableWhen the DLQ entry expires and is deleted (dead_lettered_at + 7 days).
external_idstringRequiredThe provider's message id (NIDD
message_id) or, for UDP, a hash of the IMSI, arrival time and payload. Unique per provider account.iccidstringRequiredThe device's ICCID (snapshot).
inserted_attimestampRequiredlast_attempt_attimestamp· nullablepayloadstringRequiredThe payload bytes, base64 (RFC 4648, padded). Owning customer only.
payload_sizeintegerRequiredPayload size in bytes.
received_attimestampRequiredWhen HoundSat (or the provider, for NIDD) received the message.
reseller_iduuid· nullableThe device's reseller when the message arrived (snapshot).
sourceenumRequiredsatellite | cellular. Cellular messages are stored, never forwarded.
One of
satellitecellulartransportenumRequirednidd | udp.
One of
niddudpupdated_attimestampRequired
{
"type": "inbound_message",
"id": "5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8",
"attributes": {
"attempt_count": 3,
"cancelled_endpoint_ids": [
"0b6d4e2a-1c9f-4a3b-8e5d-7f2c0a9b6d13"
],
"customer_id": "c41e9a03-6b2f-4d8e-a571-0f3b9c2d7e14",
"dead_lettered_at": null,
"dead_lettered_endpoint_ids": [
"0b6d4e2a-1c9f-4a3b-8e5d-7f2c0a9b6d13"
],
"delivered_at": "2026-10-12T14:03:21Z",
"delivered_endpoint_ids": [
"0b6d4e2a-1c9f-4a3b-8e5d-7f2c0a9b6d13"
],
"delivery_status": "delivered",
"device_id": "5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8",
"dlq": false,
"endpoint_ids": [
"0b6d4e2a-1c9f-4a3b-8e5d-7f2c0a9b6d13"
],
"expires_at": null,
"external_id": "trailer-207",
"iccid": "8999001234000020718",
"inserted_at": "2026-10-12T14:03:21Z",
"last_attempt_at": "2026-10-12T14:03:21Z",
"payload": "dGVtcD0yMS41",
"payload_size": 9,
"received_at": "2026-10-12T14:03:21Z",
"reseller_id": "a7d35c90-2e14-4f6b-8c03-5b9e1d4a2f67",
"source": "satellite",
"transport": "nidd",
"updated_at": "2026-10-12T14:03:21Z"
}
}The delivery attempt object
One attempt to deliver a message to one webhook endpoint.
Attributes
iduuidRequiredUnique identifier.
attempt_numberintegerRequired1 for the first attempt to this endpoint, then 2, 3 (retries continue it).
attempted_attimestampRequiredcustomer_iduuid· nullableThe message's customer (snapshot).
duration_msintegerRequiredHow long the attempt took, in milliseconds.
error_codestring· nullableWhy the attempt failed (null on success): http_<status>, timeout, connection_refused, dns_error, tls_error, blocked_target, other.
http_statusinteger· nullableThe HTTP status the endpoint answered; null when no response arrived.
inbound_message_iduuidRequiredinserted_attimestampRequiredreseller_iduuid· nullableThe message's reseller (snapshot).
succeededbooleanRequiredThe endpoint answered 2xx.
webhook_endpoint_iduuid· nullable
{
"type": "delivery_attempt",
"id": "5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8",
"attributes": {
"attempt_number": 1,
"attempted_at": "2026-10-12T14:03:21Z",
"customer_id": "c41e9a03-6b2f-4d8e-a571-0f3b9c2d7e14",
"duration_ms": 142,
"error_code": "…",
"http_status": 200,
"inbound_message_id": "3d9f1a7c-5e2b-4c80-9f64-a1b8e3d0c5f2",
"inserted_at": "2026-10-12T14:03:21Z",
"reseller_id": "a7d35c90-2e14-4f6b-8c03-5b9e1d4a2f67",
"succeeded": true,
"webhook_endpoint_id": "e2b7d9f0-4c1a-4e63-b8a5-7d0f2c9e1b36"
}
}Endpoints
List messages
/api/json/inbound_messagesMessages in your scope, newest first. Filter by device_id, delivery_status, dlq, received_at, source and transport.
- Who
- Resellers and Customers
- Roles
- Owner, Admin, Staff
filter[dlq]=true&page[count]=truegives the DLQ and its size inmeta.page.total.
Query parameters
filter[<field>]objectNarrows the list:
filter[status]=active, or with an operator:filter[inserted_at][greater_than_or_equal]=2026-10-01T00:00:00Z. Operators:in,is_nil,greater_than,greater_than_or_equal,less_than,less_than_or_equal.One of
attempt_countcustomer_iddead_lettered_atdelivered_atdelivery_statusdevice_iddlqexpires_atexternal_idiccididinserted_atlast_attempt_atpayload_sizereceived_atreseller_idsourcetransportupdated_atsortstringA field to sort by; prefix it with
-for descending, e.g.sort=-inserted_at. Separate several with commas.page[limit]integerPage size: 50 by default, at most 200.
page[after]stringThe cursor from
links.nextof the previous page.page[offset]works too.page[count]booleantrueadds the total number of matches inmeta.page.total.
curl https://api.houndsat.com/api/json/inbound_messages \
-H "Authorization: Bearer $HOUNDSAT_TOKEN"{
"data": [
{
"type": "inbound_message",
"id": "5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8",
"attributes": {
"attempt_count": 3,
"cancelled_endpoint_ids": [
"0b6d4e2a-1c9f-4a3b-8e5d-7f2c0a9b6d13"
],
"customer_id": "c41e9a03-6b2f-4d8e-a571-0f3b9c2d7e14",
"dead_lettered_at": null,
"dead_lettered_endpoint_ids": [
"0b6d4e2a-1c9f-4a3b-8e5d-7f2c0a9b6d13"
],
"delivered_at": "2026-10-12T14:03:21Z",
"delivered_endpoint_ids": [
"0b6d4e2a-1c9f-4a3b-8e5d-7f2c0a9b6d13"
],
"delivery_status": "delivered",
"device_id": "5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8",
"dlq": false,
"endpoint_ids": [
"0b6d4e2a-1c9f-4a3b-8e5d-7f2c0a9b6d13"
],
"expires_at": null,
"external_id": "trailer-207",
"iccid": "8999001234000020718",
"inserted_at": "2026-10-12T14:03:21Z",
"last_attempt_at": "2026-10-12T14:03:21Z",
"payload": "dGVtcD0yMS41",
"payload_size": 9,
"received_at": "2026-10-12T14:03:21Z",
"reseller_id": "a7d35c90-2e14-4f6b-8c03-5b9e1d4a2f67",
"source": "satellite",
"transport": "nidd",
"updated_at": "2026-10-12T14:03:21Z"
}
}
],
"links": {
"next": "/api/json/inbound_messages?page[after]=g2wAAAABbQAAACQ1ZjBj…"
}
}Get a message
/api/json/inbound_messages/{id}One message in your scope.
- Who
- Resellers and Customers
- Roles
- Owner, Admin, Staff
Path parameters
iduuidRequiredThe record's id.
curl https://api.houndsat.com/api/json/inbound_messages/5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8 \
-H "Authorization: Bearer $HOUNDSAT_TOKEN"{
"data": {
"type": "inbound_message",
"id": "5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8",
"attributes": {
"attempt_count": 3,
"cancelled_endpoint_ids": [
"0b6d4e2a-1c9f-4a3b-8e5d-7f2c0a9b6d13"
],
"customer_id": "c41e9a03-6b2f-4d8e-a571-0f3b9c2d7e14",
"dead_lettered_at": null,
"dead_lettered_endpoint_ids": [
"0b6d4e2a-1c9f-4a3b-8e5d-7f2c0a9b6d13"
],
"delivered_at": "2026-10-12T14:03:21Z",
"delivered_endpoint_ids": [
"0b6d4e2a-1c9f-4a3b-8e5d-7f2c0a9b6d13"
],
"delivery_status": "delivered",
"device_id": "5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8",
"dlq": false,
"endpoint_ids": [
"0b6d4e2a-1c9f-4a3b-8e5d-7f2c0a9b6d13"
],
"expires_at": null,
"external_id": "trailer-207",
"iccid": "8999001234000020718",
"inserted_at": "2026-10-12T14:03:21Z",
"last_attempt_at": "2026-10-12T14:03:21Z",
"payload": "dGVtcD0yMS41",
"payload_size": 9,
"received_at": "2026-10-12T14:03:21Z",
"reseller_id": "a7d35c90-2e14-4f6b-8c03-5b9e1d4a2f67",
"source": "satellite",
"transport": "nidd",
"updated_at": "2026-10-12T14:03:21Z"
}
}
}Retry a message
/api/json/inbound_messages/{id}/retrySends a dead-lettered message again to each of its failed endpoints that is still enabled, with three new attempts. An endpoint that already accepted it never gets it twice.
- Who
- Resellers and Customers
- Roles
- Owner, Admin
- Refused with
422 retry_refusedwhen the message isn't in the DLQ, has expired, or its endpoint was disabled or deleted.meta.reasonsays which:not_in_dlq,expired,no_enabled_endpointorstill_running.
Path parameters
iduuidRequiredThe record's id.
curl -X PATCH https://api.houndsat.com/api/json/inbound_messages/5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8/retry \
-H "Authorization: Bearer $HOUNDSAT_TOKEN" \
-H "Content-Type: application/vnd.api+json" \
-d '{"data":{"type":"inbound_message","id":"5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8","attributes":{}}}'{
"data": {
"type": "inbound_message",
"id": "5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8",
"attributes": {
"attempt_count": 3,
"cancelled_endpoint_ids": [
"0b6d4e2a-1c9f-4a3b-8e5d-7f2c0a9b6d13"
],
"customer_id": "c41e9a03-6b2f-4d8e-a571-0f3b9c2d7e14",
"dead_lettered_at": null,
"dead_lettered_endpoint_ids": [
"0b6d4e2a-1c9f-4a3b-8e5d-7f2c0a9b6d13"
],
"delivered_at": "2026-10-12T14:03:21Z",
"delivered_endpoint_ids": [
"0b6d4e2a-1c9f-4a3b-8e5d-7f2c0a9b6d13"
],
"delivery_status": "delivered",
"device_id": "5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8",
"dlq": false,
"endpoint_ids": [
"0b6d4e2a-1c9f-4a3b-8e5d-7f2c0a9b6d13"
],
"expires_at": null,
"external_id": "trailer-207",
"iccid": "8999001234000020718",
"inserted_at": "2026-10-12T14:03:21Z",
"last_attempt_at": "2026-10-12T14:03:21Z",
"payload": "dGVtcD0yMS41",
"payload_size": 9,
"received_at": "2026-10-12T14:03:21Z",
"reseller_id": "a7d35c90-2e14-4f6b-8c03-5b9e1d4a2f67",
"source": "satellite",
"transport": "nidd",
"updated_at": "2026-10-12T14:03:21Z"
}
}
}Retry messages in bulk
/api/json/inbound_messages/bulk/retryRetries up to 500 dead-lettered messages, with one result per id (and a reason for each refusal).
- Who
- Resellers and Customers
- Roles
- Owner, Admin
Body
Sent directly under data.
idsarray of uuidRequired
curl -X POST https://api.houndsat.com/api/json/inbound_messages/bulk/retry \
-H "Authorization: Bearer $HOUNDSAT_TOKEN" \
-H "Content-Type: application/vnd.api+json" \
-d '{"data":{"ids":["5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8"]}}'{
"results": [
{
"code": null,
"id": "5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8",
"message": null,
"reason": null,
"status": "ok"
}
]
}List delivery attempts
/api/json/delivery_attemptsDelivery attempts in your scope, oldest first. Filter by inbound_message_id for one message's history, or by webhook_endpoint_id, error_code and attempted_at.
- Who
- Resellers and Customers
- Roles
- Owner, Admin, Staff
Query parameters
filter[<field>]objectNarrows the list:
filter[status]=active, or with an operator:filter[inserted_at][greater_than_or_equal]=2026-10-01T00:00:00Z. Operators:in,is_nil,greater_than,greater_than_or_equal,less_than,less_than_or_equal.One of
attempt_numberattempted_atcustomer_idduration_mserror_codehttp_statusidinbound_message_idinserted_atreseller_idsucceededwebhook_endpoint_idsortstringA field to sort by; prefix it with
-for descending, e.g.sort=-inserted_at. Separate several with commas.page[limit]integerPage size: 50 by default, at most 200.
page[after]stringThe cursor from
links.nextof the previous page.page[offset]works too.page[count]booleantrueadds the total number of matches inmeta.page.total.
curl https://api.houndsat.com/api/json/delivery_attempts \
-H "Authorization: Bearer $HOUNDSAT_TOKEN"{
"data": [
{
"type": "delivery_attempt",
"id": "5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8",
"attributes": {
"attempt_number": 1,
"attempted_at": "2026-10-12T14:03:21Z",
"customer_id": "c41e9a03-6b2f-4d8e-a571-0f3b9c2d7e14",
"duration_ms": 142,
"error_code": "…",
"http_status": 200,
"inbound_message_id": "3d9f1a7c-5e2b-4c80-9f64-a1b8e3d0c5f2",
"inserted_at": "2026-10-12T14:03:21Z",
"reseller_id": "a7d35c90-2e14-4f6b-8c03-5b9e1d4a2f67",
"succeeded": true,
"webhook_endpoint_id": "e2b7d9f0-4c1a-4e63-b8a5-7d0f2c9e1b36"
}
}
],
"links": {
"next": "/api/json/delivery_attempts?page[after]=g2wAAAABbQAAACQ1ZjBj…"
}
}Get a delivery attempt
/api/json/delivery_attempts/{id}One delivery attempt in your scope.
- Who
- Resellers and Customers
- Roles
- Owner, Admin, Staff
Path parameters
iduuidRequiredThe record's id.
curl https://api.houndsat.com/api/json/delivery_attempts/5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8 \
-H "Authorization: Bearer $HOUNDSAT_TOKEN"{
"data": {
"type": "delivery_attempt",
"id": "5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8",
"attributes": {
"attempt_number": 1,
"attempted_at": "2026-10-12T14:03:21Z",
"customer_id": "c41e9a03-6b2f-4d8e-a571-0f3b9c2d7e14",
"duration_ms": 142,
"error_code": "…",
"http_status": 200,
"inbound_message_id": "3d9f1a7c-5e2b-4c80-9f64-a1b8e3d0c5f2",
"inserted_at": "2026-10-12T14:03:21Z",
"reseller_id": "a7d35c90-2e14-4f6b-8c03-5b9e1d4a2f67",
"succeeded": true,
"webhook_endpoint_id": "e2b7d9f0-4c1a-4e63-b8a5-7d0f2c9e1b36"
}
}
}