> For the complete documentation index, see [llms.txt](https://archer-bot.gitbook.io/archer.bot/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://archer-bot.gitbook.io/archer.bot/publish-on-archer/partner-dispatch-contracts.md).

# Dispatch Contracts

> Companion to [Publishing a Partner Intent](/archer.bot/publish-on-archer/publishing-a-partner-intent.md). That guide covers onboarding; this page is the **wire contract** you implement on `POST` (not GET).

> ## Verifying Archer's signature is mandatory
>
> **You cannot go live until your endpoint verifies the `archerSignature` on every dispatch.** At activation, Archer sends your endpoint a deliberately **unsigned / forged** request. If your endpoint accepts it (does not return `401`/`403`), Archer will **not** activate your intent.
>
> **Why this is required:** your `toolEndpoint` is a public URL. Anyone who discovers it can POST to it. A signed request is the only way to know a call genuinely came from Archer. Without verification, anyone could call your endpoint directly, run up your provider costs, and bypass the per-call payment and attribution model entirely. The signature is **asymmetric**: you verify with Archer's published public key (`/.well-known/archer-public-key`) and never hold any secret that could impersonate Archer. Verification details (canonical JSON, the public-key endpoint, and a copy-paste verifier) are in [Publishing a Partner Intent → Verify the signature](/archer.bot/publish-on-archer/publishing-a-partner-intent.md#step-3-verify-the-signature).

## REST is always `POST`

Archer never calls your endpoint with GET query strings. Every dispatch is:

```
POST <your toolEndpoint>
Content-Type: application/json
Idempotency-Key: <requestId>
```

The body is the signed [request envelope](/archer.bot/publish-on-archer/publishing-a-partner-intent.md#step-2-the-request-envelope). You verify `archerSignature`, read `args`, run your logic, and return the [response envelope](/archer.bot/publish-on-archer/publishing-a-partner-intent.md#step-4-the-response-envelope).

## What is in `args`?

`args` is a **JSON object**. Its shape depends on **which Archer consumer path** invoked the intent, not on HTTP verb.

| Path                                                                | When                                                                                    | What Archer sends in `args`                                                                                                                            |
| ------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **AgentLoop** (`@handle` in chat, non-flat-fee or LLM-routed)       | The user's message is classified; the LLM calls your tool using your `parametersSchema` | Structured fields your schema declares (e.g. `{ "ticker": "ETH" }`). Validated against `parametersSchema` before dispatch.                             |
| **Flat-fee gate** (`feeRateUnit` = `flat_stablecoin` / `flat_usdc`) | Pay-first partner (e.g. `@DemoAnalyst` on test)                                         | **Opaque** `{ "query": "<user text minus @handle>" }` only. Archer does **not** parse tickers, timeframes, or portfolio fields. You own NL → your API. |

Both paths use the **same** signed envelope and `POST`. The difference is who produces `args` (LLM vs opaque pass-through).

### Example: AgentLoop (structured)

User: `@TokenAnalyzer what's the price of ETH?`

```json
{
  "requestId": "…",
  "intentDefinitionId": "…",
  "userId": "…",
  "args": { "ticker": "ETH" },
  "timestamp": 1715712345678,
  "archerSignature": "…"
}
```

### Example: Flat-fee (opaque)

User: `@DemoAnalyst chart ETH over the last 30 days`

After EIP-3009 payment, Archer dispatches:

```json
{
  "args": { "query": "chart ETH over the last 30 days" }
}
```

Your server parses the opaque `query` into its own schema. Archer never inspects it.

## Flat-fee payment rail

Flat-fee intents add a **pay-before-dispatch** step on Archer's side:

1. The user sees a quote (USDC on Base Sepolia, gross amount, partner + Archer split).
2. The user signs **EIP-3009** `ReceiveWithAuthorization` into `ArcherFlatFeePayer`.
3. Archer submits on-chain, then **POSTs** the opaque `{ query }` envelope to your REST endpoint.

Your REST handler is unchanged: same signature verification, same response shape. Payment is between the user and Archer's contracts; you are paid via the attestation split configured on the `IntentDefinition` row.

**Test public key:** `https://archer-api-test.up.railway.app/.well-known/archer-public-key`

## Can you receive the user's portfolio (or other context)?

**Today (v1):** No first-party enrichment. Archer forwards:

* `userId` (opaque pseudonymous id in the envelope)
* `args` (structured or `{ query }` only)
* `requestId` for idempotency

Archer does **not** attach wallet balances, holdings, or thread history to partner REST calls unless those appear inside `query` text or in LLM-produced `args` fields your schema asks for.

**Not read-only:** REST dispatch is not limited to "GET-style" reads. You can execute writes, call paid APIs, or return rich JSON + `archerEmbed` charts. The constraint is **what Archer puts in `args`**, not HTTP semantics.

**Future:** Optional **context payloads** the user opts into (e.g. portfolio snapshot, risk profile) as extra envelope fields or structured `args` keys, with explicit consent, redaction, and builder disclosure in the marketplace UI. Requires:

* Schema/versioning for `inputContract` / context attachments
* Privacy review (what leaves Archer's boundary)
* Builder opt-in per intent

Until that ships, if you need portfolio-aware analysis, the user must describe holdings in natural language (opaque `query`) or you declare explicit optional fields in `parametersSchema` for the AgentLoop path.

## MCP\_HTTP binding

Same `args` semantics. Archer connects as an MCP client and `callTool(publicIdentifier, args)`. Signature material is forwarded on HTTP headers (`X-Archer-Signature`, etc.). See [Publishing a Partner Intent: MCP\_HTTP](/archer.bot/publish-on-archer/publishing-a-partner-intent.md#mcp_http-recommended-if-you-already-run-an-mcp-server).

## Reference implementations

| Artifact                            | Location                                                                                                                  |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| Partner verifier (Node + Python)    | [`archer-partner-examples`](https://github.com/Archer-Laboratories/archer-partner-examples)                               |
| MCP\_HTTP header verification notes | [`mcp-verifier-notes.md`](https://github.com/Archer-Laboratories/archer-partner-examples/blob/main/mcp-verifier-notes.md) |

## Roadmap

* Public developer docs (this page)
* Published verifier libraries (npm / PyPI)
* Flat-fee on-chain payment rail
* Structured input / context contracts (future)
