agrirouterDocs

Sending & Receiving

How to send and receive messages through agrirouter in production integrations, with addressing, retries, chunk reassembly, and feed confirmation

This page covers the integration-level concerns around sending and receiving messages: addressing modes, retries, chunk reassembly on the receive side, and feed confirmation. The walkthrough-style introductions live in the getting-started tutorials and are the right starting point the first time through.

For the conceptual messaging model, see Messaging.

Sending a payload end to end

Loading diagram...

Every outbound message is a single HTTPS request to POST /messages. The envelope is carried in HTTP headers, and the payload is the raw request body with Content-Type: application/octet-stream. See Send Your First Message for the full header reference.

If your integration uses generated clients, start from the hosted OpenAPI specification and check how your generator represents binary request bodies, repeated headers, and SSE streams.

A successful 200 response means agrirouter has accepted the message for routing. It does not confirm delivery to any recipient. There is no asynchronous delivery acknowledgement event for the sender.

POST /messages — API playground1 min

Addressing modes

Choose between direct addressing and publish on a per-message basis using two headers.

Set x-agrirouter-is-publish: false and provide x-agrirouter-direct-recipients with a comma-separated list of recipient endpoint UUIDs. The message is delivered only to those endpoints, provided a route exists from the sender to each recipient for this message type.

Use direct addressing for interactive flows where the user picks the target, for example sending an application map from an FMIS to a specific machine.

Retries

POST /messages has no idempotency key, and agrirouter does not deduplicate. Every request that is accepted creates a new message in the feed of each routed recipient, and the delivered message carries no application-side identifier that the recipient could use to detect a repeat. Retry decisions are therefore made on the HTTP outcome alone:

OutcomeRetry?Notes
200NoThe message is in the recipients' feeds. Sending it again delivers a duplicate.
No response (connection error, timeout)YesThe outcome is unknown. A retry may produce a duplicate on the receiving side, which the recipient has to tolerate.
429, 5xxYes, with backoffThe message was not accepted. Back off exponentially between attempts.
400, 403, 413NoThe request is invalid as-is. Fix it before sending again, and read the 400 message text first: a partial-delivery 400 means some recipients already received the message (see below).

The gateway does not return a Retry-After header on 429, so the backoff schedule is up to the client. See Rate limits for how the limits are scoped and Limitations for the current values.

Multi-recipient direct sends are not atomic

When x-agrirouter-direct-recipients names several endpoints, agrirouter evaluates routes and capabilities per recipient, delivers to every recipient that passes, and only then reports the failures:

  • No recipient is routable: nothing is delivered. The response is 400 with the message No recipients for this sender and info type.
  • Some recipients are routable: the message is delivered to those recipients. The response is still 400, with the message Recipient is not allowed from this sender.

A 400 on a multi-recipient send therefore does not mean that nothing was delivered. Resending the same payload to the full recipient list after a partial failure delivers a duplicate to every recipient that already received it. Either send to each recipient in its own request, or resend only to the rejected recipients once the account owner has fixed the routes. See Routing failures for the exact messages.

Chunking

Payloads larger than the transport chunk size are split by agrirouter on the way out. The sender does not implement chunking. The content-length header must carry the total payload size in bytes; the API uses it to decide whether and how to split. agrirouter generates the chunk context that ties the chunks together, and the recipient receives the reassembled file as a single FILE_RECEIVED event.

The maximum payload size accepted by the API is 256 MB. Payloads exceeding that limit are rejected with 413.

Receiving through the SSE stream

Events are delivered through a single Server-Sent Events stream. Open GET /events with a valid access token and keep the connection open; events stream as they occur. There is no polling API, no webhook callback, and no push-notification fallback.

The stream covers two families of events. The data-flow events tell you about messages and files arriving on your feeds; the tenant-state events tell you about authorizations and endpoint visibility:

EventMeaning
MESSAGE_RECEIVEDA new message has arrived in the feed of one of your endpoints.
FILE_RECEIVEDA chunked file payload (TaskData, Shape, PDF, image, video) has been fully reassembled by agrirouter and is ready to download.
ENDPOINT_DELETEDOne of your endpoints was deleted, either by your application or by the account owner.
ENDPOINTS_LIST_CHANGEDThe set of endpoints visible to your application in a tenant changed, or a visible endpoint's capabilities or routes changed.
AUTHORIZATION_ADDEDA user granted your application a new authorization for a tenant.
AUTHORIZATION_REVOKEDA user revoked an authorization. Access to the tenant for the given scope is already gone when the event is delivered.

See Receive Your First Message for the wire format and a sample MESSAGE_RECEIVED event, and the Events catalog for the per-event payloads.

GET /events — API playground1 min

Keeping the connection open

While the stream is idle, the gateway writes an SSE comment line (: keep-alive) every 5 seconds. Comment lines carry no event and are ignored by SSE clients, but they let you detect a dead connection: if nothing at all arrives for well over 5 seconds (for example 30 seconds), close the connection and reconnect. Configure idle timeouts on HTTP clients and proxies accordingly, and disable response buffering on any proxy between you and agrirouter.

Replay on reconnect

Long-lived SSE connections are the simplest model, but many integrations cannot hold one open: batch jobs, mobile clients, processes that cycle through restarts. When you open a fresh SSE connection, the gateway replays every unconfirmed message in your endpoints' feeds, then continues with live events. The replay starts a few seconds after the connection is established and walks the feeds in pages, oldest first, until it has caught up with the live stream. You do not need a cursor; reconnecting is enough to catch up.

Confirming events promptly matters: confirmed events drop out of the replay window, so the next reconnect only replays genuinely missed events.

Downloading the payload

Both MESSAGE_RECEIVED and FILE_RECEIVED events may include the payload directly as payload (base64 inline, for small payloads) or as a payload_uri link to download (for larger payloads). Exactly one of the two is present.

payload_uri links are time-limited and expire after at most 15 minutes. Download the payload as soon as you receive the event.

The payload_uri is pre-signed and does not need an Authorization header. The response body is the raw binary (application/octet-stream) in the original format.

Chunked file reassembly

For chunked message types (TaskData, Shape, PDF, images, videos), agrirouter reassembles the chunks server-side and emits a single FILE_RECEIVED event when the whole payload has arrived. The event carries a message_ids array listing the agrirouter message IDs of the individual chunks, plus one payload_uri (or inline payload) for the reassembled file.

Your application does not see the intermediate chunks as separate events. You download the reassembled payload once and confirm every ID in message_ids so the chunks drop out of the feed.

Confirming messages

Every event you handle must be confirmed with POST /confirmations, otherwise it keeps replaying on reconnect and accumulates in your endpoint's feed. A confirmation is a (endpoint_id, message_id) pair; a single request can carry many.

A successful 202 means the confirmation was accepted for processing. The feed is updated asynchronously, so a just-confirmed message may still appear in the replay window for a brief interval.

POST /confirmations — API playground1 min

Unconfirmed events accumulate in your feed and will replay on every reconnect. Confirm events after your application has processed them.

Error handling

ErrorCauseResolution
400 on sendMalformed headers, unsupported message type, or missing required field.Validate the request against the API spec before retrying.
400 on send, No recipients for this sender and info typeNo direct recipient is reachable: no route exists from the sender, or the recipient lacks the capability. Nothing was delivered.Ask the account owner to configure a route for the sender, recipient, and message type.
400 on send, Recipient is not allowed from this senderSome, but not all, direct recipients are reachable. The message was delivered to the reachable ones.Do not resend to the full list. See Multi-recipient direct sends.
429 on sendThe application exceeded its request rate.Back off exponentially and retry. No Retry-After header is sent. See Rate limits.
403 on sendThe endpoint ID in the request does not belong to a tenant the access token is authorized for.Confirm the access token matches the endpoint's tenant; confirm the endpoint has not been deleted.
413 on sendPayload exceeds the 256 MB limit.Split the data at the application level before sending.
Event not arrivingThe recipient's capabilities or the account's routes do not cover the message type.Verify the recipient's capabilities and confirm the account owner has configured a route.
payload_uri returns 403 or 404The 15-minute expiry window has passed.Trigger a fresh event, or re-request via the SSE stream on reconnect.

See Errors for the full HTTP status code list and response body shape.

Next steps

On this page