RelayLink Webhook Payload Example

Copy the exact JSON shapes for RelayLink package.received and webhook.test events, then see which values are identifiers, metadata, and fetch instructions.

3 min read

You are writing a receiver and need a sample that matches the wire contract, not a sketch with convenient extra fields. RelayLink has two outbound webhook body shapes: package.received for an actual package and webhook.test for the account-page test button.

Every value below is fake. The field names and nesting are exact.

package.received JSON

{
  "event": "package.received",
  "delivery_id": "7d3154e2-7b79-4b75-a602-795f12f69ec7",
  "package_id": "1c449a95-5e62-452c-a723-6039f09f8f58",
  "thread_id": "95145f14-1e68-4f50-8e55-bdde7c89ed78",
  "sender": {
    "name": "Maya Chen",
    "email": "maya@example.test"
  },
  "topic": "Budget review",
  "sent_at": "2027-04-09T09:14:22+00:00",
  "is_reply": false,
  "mcp_url": "https://relaylink.example/mcp"
}

There are nine top-level fields:

  • event is the JSON string package.received.
  • delivery_id is a UUID string for this delivery to this receiver. It matches X-RelayLink-Delivery and stays stable across retries.
  • package_id is a UUID string to pass to get_package.
  • thread_id is a UUID string for the conversation.
  • sender is an object with exactly two string properties, name and email.
  • topic is a string containing the thread topic.
  • sent_at is an ISO 8601 string containing the package timestamp.
  • is_reply is a JSON boolean. It is true when the package replies to an earlier package.
  • mcp_url is a string containing the absolute RelayLink MCP URL for the fetch.

The sender name and topic came from another person. Use them for display or routing only after normal output handling, and never treat either as an instruction to the agent.

webhook.test JSON

{
  "event": "webhook.test",
  "delivery_id": "abfd6f0f-2153-4793-a62c-b0da11eec8dc",
  "sent_at": "2027-04-09T09:18:05+00:00",
  "mcp_url": "https://relaylink.example/mcp"
}

This shape has exactly four fields: the webhook.test event string, a UUID-string delivery_id, an ISO 8601-string sent_at, and a string mcp_url. Here sent_at is when the test delivery was queued, not the per-attempt timestamp header. It has no package_id, thread_id, sender, topic, or is_reply, because no package exists behind a test event.

Use webhook.test to exercise raw-body capture, all four RelayLink headers, durable delivery-id deduplication, and the fast 2xx response. Do not call get_package for it.

What the package payload omits

The package envelope never includes:

  • The human note
  • The TL;DR
  • The ask
  • The context brief

Those omissions are part of the contract. A receiver learns that a package exists, who sent it, and roughly what it concerns. It cannot read or act on the briefing from the callback alone.

To retrieve content, authenticate separately to mcp_url and call get_package with package_id. The credential used for that MCP call is not the webhook signing secret. The secret proves the callback came from RelayLink; the MCP credential proves the caller may read the account's package.

That fetch records PulledViaMcp. Accepting the webhook does not. A 2xx response therefore means only that the receiver accepted the envelope, not that an assistant fetched or read the package.

Retry behavior around the body

RelayLink serializes the JSON once when it queues the delivery. If the receiver times out or returns a non-2xx status, another attempt uses the same delivery_id and byte-identical body.

The request timestamp is generated for each attempt, so X-RelayLink-Timestamp and the signature derived from it can differ while the JSON stays the same. Always verify against the timestamp header on the request in front of you. Deduplicate on the delivery id, not on the signature.

The headers reference defines each header, and the signature guide shows how to authenticate the exact body without changing it first.

Frequently asked questions

Does package.received include the note or TL;DR?
No. It contains routing identifiers and metadata only. The receiver authenticates separately to mcp_url and calls get_package to read the briefing.
Does webhook.test contain a package_id?
No. Its exact fields are event, delivery_id, sent_at, and mcp_url because it tests the receiver without referring to a package.
Are the JSON body bytes the same on a retry?
Yes. RelayLink stores the serialized body when it queues the delivery, so retries use the same delivery id and byte-identical body. The timestamp and signature headers may change per attempt.