Skip to content
Docs

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.

The message object

One message from a device and the state of its deliveries.

Attributes

  • iduuidRequired

    Unique identifier.

  • attempt_countintegerRequired

    Delivery attempts so far, over all endpoints.

  • cancelled_endpoint_idsarray of uuidRequired

    Endpoints disabled or deleted before they accepted the message.

  • customer_iduuid· nullable

    The device's customer when the message arrived (snapshot); null: none.

  • dead_lettered_attimestamp· nullable

    When it moved to the DLQ.

  • dead_lettered_endpoint_idsarray of uuidRequired

    Endpoints that failed all their attempts.

  • delivered_attimestamp· nullable

    When the last endpoint accepted it (status delivered).

  • delivered_endpoint_idsarray of uuidRequired

    Endpoints that accepted the message (2xx).

  • delivery_statusenumRequired

    pending | retrying | delivered | dead_lettered | no_endpoint | not_forwarded.

    One ofpendingretryingdelivereddead_letteredno_endpointnot_forwarded

  • device_iduuidRequired
  • dlqboolean· nullable

    In the dead-letter queue (filter[dlq]=true).

  • endpoint_idsarray of uuidRequired

    The customer's enabled webhook endpoints when the message arrived.

  • expires_attimestamp· nullable

    When the DLQ entry expires and is deleted (dead_lettered_at + 7 days).

  • external_idstringRequired

    The provider's message id (NIDD message_id) or, for UDP, a hash of the IMSI, arrival time and payload. Unique per provider account.

  • iccidstringRequired

    The device's ICCID (snapshot).

  • inserted_attimestampRequired
  • last_attempt_attimestamp· nullable
  • payloadstringRequired

    The payload bytes, base64 (RFC 4648, padded). Owning customer only.

  • payload_sizeintegerRequired

    Payload size in bytes.

  • received_attimestampRequired

    When HoundSat (or the provider, for NIDD) received the message.

  • reseller_iduuid· nullable

    The device's reseller when the message arrived (snapshot).

  • sourceenumRequired

    satellite | cellular. Cellular messages are stored, never forwarded.

    One ofsatellitecellular

  • transportenumRequired

    nidd | udp.

    One ofniddudp

  • updated_attimestampRequired
The inbound message object
{
  "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

  • iduuidRequired

    Unique identifier.

  • attempt_numberintegerRequired

    1 for the first attempt to this endpoint, then 2, 3 (retries continue it).

  • attempted_attimestampRequired
  • customer_iduuid· nullable

    The message's customer (snapshot).

  • duration_msintegerRequired

    How long the attempt took, in milliseconds.

  • error_codestring· nullable

    Why the attempt failed (null on success): http_<status>, timeout, connection_refused, dns_error, tls_error, blocked_target, other.

  • http_statusinteger· nullable

    The HTTP status the endpoint answered; null when no response arrived.

  • inbound_message_iduuidRequired
  • inserted_attimestampRequired
  • reseller_iduuid· nullable

    The message's reseller (snapshot).

  • succeededbooleanRequired

    The endpoint answered 2xx.

  • webhook_endpoint_iduuid· nullable
The delivery attempt object
{
  "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

get/api/json/inbound_messages

Messages 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]=true gives the DLQ and its size in meta.page.total.

Query parameters

  • filter[<field>]object

    Narrows 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 ofattempt_countcustomer_iddead_lettered_atdelivered_atdelivery_statusdevice_iddlqexpires_atexternal_idiccididinserted_atlast_attempt_atpayload_sizereceived_atreseller_idsourcetransportupdated_at

  • sortstring

    A field to sort by; prefix it with - for descending, e.g. sort=-inserted_at. Separate several with commas.

  • page[limit]integer

    Page size: 50 by default, at most 200.

  • page[after]string

    The cursor from links.next of the previous page. page[offset] works too.

  • page[count]boolean

    true adds the total number of matches in meta.page.total.

Request
curl https://api.houndsat.com/api/json/inbound_messages \
  -H "Authorization: Bearer $HOUNDSAT_TOKEN"
Response200 OK
{
  "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

get/api/json/inbound_messages/{id}

One message in your scope.

Who
Resellers and Customers
Roles
Owner, Admin, Staff

Path parameters

  • iduuidRequired

    The record's id.

Request
curl https://api.houndsat.com/api/json/inbound_messages/5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8 \
  -H "Authorization: Bearer $HOUNDSAT_TOKEN"
Response200 OK
{
  "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

patch/api/json/inbound_messages/{id}/retry

Sends 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_refused when the message isn't in the DLQ, has expired, or its endpoint was disabled or deleted. meta.reason says which: not_in_dlq, expired, no_enabled_endpoint or still_running.

Path parameters

  • iduuidRequired

    The record's id.

Request
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":{}}}'
Response200 OK
{
  "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

post/api/json/inbound_messages/bulk/retry

Retries 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
Request
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"]}}'
Response200 OK
{
  "results": [
    {
      "code": null,
      "id": "5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8",
      "message": null,
      "reason": null,
      "status": "ok"
    }
  ]
}

List delivery attempts

get/api/json/delivery_attempts

Delivery 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>]object

    Narrows 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 ofattempt_numberattempted_atcustomer_idduration_mserror_codehttp_statusidinbound_message_idinserted_atreseller_idsucceededwebhook_endpoint_id

  • sortstring

    A field to sort by; prefix it with - for descending, e.g. sort=-inserted_at. Separate several with commas.

  • page[limit]integer

    Page size: 50 by default, at most 200.

  • page[after]string

    The cursor from links.next of the previous page. page[offset] works too.

  • page[count]boolean

    true adds the total number of matches in meta.page.total.

Request
curl https://api.houndsat.com/api/json/delivery_attempts \
  -H "Authorization: Bearer $HOUNDSAT_TOKEN"
Response200 OK
{
  "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

get/api/json/delivery_attempts/{id}

One delivery attempt in your scope.

Who
Resellers and Customers
Roles
Owner, Admin, Staff

Path parameters

  • iduuidRequired

    The record's id.

Request
curl https://api.houndsat.com/api/json/delivery_attempts/5f0c2b7e-8d41-4a96-b3e2-91c7d0a4e6f8 \
  -H "Authorization: Bearer $HOUNDSAT_TOKEN"
Response200 OK
{
  "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"
    }
  }
}