> ## Documentation Index
> Fetch the complete documentation index at: https://docs.openpayload.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Send an encrypted message

> Submit a DDN envelope to a Relay using the recipient declared in the envelope

Use this endpoint after your client has resolved the recipient, encrypted the message, and built a DDN envelope. The Relay reads only the public outer fields needed for validation and delivery.

Do not send `policy_context`, `policy_id`, `policy_revision`, `policy_rule_id`, `policy_block_hash`, or `policy_block_number`. These fields are reserved network provenance, and this route rejects them with `400 client_policy_context_forbidden`.

## Request

The body is one JSON [DDN envelope](/guides/ddn-envelope).

<ParamField body="message_id" type="string" required>
  A UUID generated once for this message. Reusing it may cause deduplication.
</ParamField>

<ParamField body="to" type="string" required>
  The recipient DID or alias, optionally followed by `@persona`. `recipient` is accepted as an alternate field name. Do not append a tag.
</ParamField>

<ParamField body="tag" type="string">
  Optional public, case-sensitive message classification used by policy conditions.
</ParamField>

<ParamField body="application_id" type="string">
  Optional registered application identifier. It is separate from `tag` and does not change recipient delivery routing. If the sender signs the envelope, include it in the v2 canonical signature payload.
</ParamField>

<ParamField body="timestamp" type="string" required>
  ISO-8601, epoch milliseconds, or epoch seconds. `created_at` is accepted as an alternate.
</ParamField>

<ParamField body="payload" type="object" required>
  Your encrypted application payload.
</ParamField>

<ParamField body="encrypted_header" type="string">
  Base64 encrypted header. When used, also send `header_nonce`, `schema_version`, and a header key reference.
</ParamField>

<ParamField body="context" type="object | null">
  Optional client-defined JSON context. A client may set this field to `null` or omit it. When non-null, it must be a JSON object, but Relay does not interpret or impose an application schema on its contents.
</ParamField>

<ParamField body="message_group_id" type="string">
  UUID shared by an assured chunk set. When present, also send `sequence_number` and `total_chunks`.
</ParamField>

<ParamField body="sequence_number" type="integer">
  Zero-based position in the assured chunk set. An identical sequence may be retried; conflicting content is rejected.
</ParamField>

<ParamField body="total_chunks" type="integer">
  Number of envelopes the sender commits to submit for this set. Every envelope in the set must declare the same value.
</ParamField>

<RequestExample>
  ```bash Request theme={null}
  curl --request POST \
    --url "https://relay.example.com/relay" \
    --header "Content-Type: application/json" \
    --data '{
      "message_id": "9f8d6f08-bce5-4df5-b22b-bf8c6bd4e143",
      "to": "did:openpayload:1111111111111111111111@example.com",
      "tag": "Legal",
      "timestamp": "<current-RFC3339-timestamp>",
      "ttl": "24h",
      "delivery_hint": "standard",
      "payload": {
        "ciphertext_b64": "<encrypted-message>"
      },
      "encrypted_header": "<base64-encrypted-header>",
      "header_nonce": "<base64-nonce>",
      "header_key_id": "recipient-key-1",
      "schema_version": "1"
    }'
  ```
</RequestExample>

## Response

<ResponseField name="success" type="boolean" required>
  Whether the Relay completed the reported delivery mode.
</ResponseField>

<ResponseField name="delivery_mode" type="string" required>
  The successful mode or `failed`. `policy_handoff` means another Relay accepted responsibility for the remaining plan; it does not confirm later Store or Archive completion.
</ResponseField>

<ResponseField name="http_status" type="integer" required>
  The HTTP status represented by the result.
</ResponseField>

<ResponseField name="code" type="string" required>
  A stable machine-readable result code.
</ResponseField>

<ResponseField name="message" type="string" required>
  Human-readable result summary. Branch on `code`, not this text.
</ResponseField>

<ResponseField name="retry_count" type="integer" required>
  Downstream delivery attempts represented by this response, including the first attempt. The field name is retained for compatibility.
</ResponseField>

<ResponseField name="cache_attempted" type="boolean" required>
  Whether the Relay made an actual Cache HTTP attempt, including for a policy `Store` step. This remains `true` when every attempted Cache rejects or fails.
</ResponseField>

<ResponseField name="cache_result" type="string | null">
  Cache outcome when applicable. Common values are `accepted`, `attempted`, `failed`, and `no_cache_endpoints`; a failed base-DID Cache request can instead expose that Cache's textual response. Treat this as diagnostic text and branch on `code` instead.
</ResponseField>

<ResponseField name="details" type="object" required>
  Policy provenance and endpoints used by policy execution. `accepted_caches` and `accepted_archives` distinguish observed Store and Archive accepts. Base-DID direct delivery can return an empty object; base Cache fallback can return `accepted_caches` and `attempted_caches`.
</ResponseField>

<ResponseExample>
  ```json 202 Response theme={null}
  {
    "success": true,
    "delivery_mode": "policy_delivery",
    "http_status": 202,
    "code": "policy_applied",
    "message": "Winning delivery policy completed.",
    "retry_count": 2,
    "cache_attempted": true,
    "cache_result": "accepted",
    "details": {
      "policy_id": "0x<64-hex-policy-id>",
      "policy_revision": 4,
      "rule_id": "legal-delivery",
      "policy_source": "persona",
      "block_hash": "0x<finalized-block-hash>",
      "forwarded_endpoints": [
        "https://inspection.example.com"
      ],
      "accepted_caches": [
        "https://cache.example.com",
        "https://cache-2.example.com"
      ],
      "accepted_archives": []
    }
  }
  ```
</ResponseExample>

For policy delivery, `details.accepted_caches` contains Store endpoints observed by the responding Relay, and `details.accepted_archives` contains Archive endpoints. Neither array aggregates storage accepted later after a `Forward` step.

For assured chunk submission, `202 chunk_staged` means the Relay durably accepted this envelope and is waiting for the remaining declared sequences. `202 chunk_set_delivered` means the complete set was delivered. Staging and completed-sequence progress survive a normal Relay process restart. If delivery is interrupted, retry an identical outstanding chunk before `details.retry_until`; the Relay resumes with the undelivered sequences. The Relay purges a set that remains incomplete past that time.

## Common failures

| Status | Meaning |
| - | - |
| `400` | Invalid JSON, missing field, unknown field, malformed DID, UUID, TTL, header or chunk metadata, or client-supplied policy transport metadata |
| `403` | A supplied sender signature or delivery-policy authorization check failed, or the winning action is `Reject/v1` |
| `404` | Recipient or required public service information was not found |
| `409` | A chunk set changed `total_chunks`, or a sequence was reused with different content |
| `413` | Envelope, header, payload, or chunk exceeds the node's advertised limit |
| `422` | Message, Persona, constraint, or winning delivery plan is not deliverable |
| `429` | Relay is rate-limiting requests or its assured-staging capacity is full |
| `502`/`503` | An upstream destination or required service is unavailable |
| `507` | The Relay could not durably persist assured staging state |

## Straight to the point

```http theme={null}
POST /relay
Content-Type: application/json

<DDN envelope whose to field is did-or-alias[@persona]>
```

* Keep `tag` in its top-level field
* Omit `policy_context` and all `policy_*` fields
* No matching policy falls through to ordinary DID services
* A failed policy lookup does not fall through


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.