> For the complete documentation index, see [llms.txt](https://docs.goassay.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.goassay.ai/developers/list-your-api-get-started/reject-reasons.md).

# Reject reasons

Identify every signature rejection and fix the request path or verifier setup.

The ready-made Worker and middleware reject invalid signatures before your application handles the call. Read the reason in the verifier's response to find the failing check.

## What the caller sees

A signature rejection returns HTTP `401`, `content-type: application/json` and a body like this:

```json
{"error":"invalid_assay_signature","reason":"BAD_SIGNATURE"}
```

This is the response sent to the direct HTTP caller, normally Assay. It is not a promise that a customer's AI app displays the same body. If you use the core library without middleware, your code must turn its failed result into an HTTP response.

## Signature rejection reasons

All eight reasons use the `401` response above. Checks stop at the first failure, so fixing one can reveal another.

| Reason            | Why the verifier rejects the request                                                                                                                                                                                                                                      | How to fix it                                                                                                                                                                                                                  |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `MISSING`         | `Signature` or `Signature-Input` is absent or empty, either header lacks the `assay` member, or a covered `Content-Digest` is missing.                                                                                                                                    | Check that the request comes through Assay and that your proxy forwards all three headers. An ordinary unsigned call should fail.                                                                                              |
| `MALFORMED`       | Unparseable headers; wrong signature value type; missing or invalid algorithm, timestamps, nonce or key ID; wrong signed component list or order; nonempty body without signed digest; missing call ID or authority; or an invalid target such as one starting with `//`. | Preserve the headers and raw target. Require `alg="ed25519"`, integer times and nonempty nonce/key ID. Let Assay generate the signature. Pass the core verifier the original request, not reconstructed JSON or a decoded URL. |
| `WRONG_TAG`       | The tag is missing or is not `assay-request`.                                                                                                                                                                                                                             | Use an Assay provider request signature. A signature made for a different purpose must remain rejected.                                                                                                                        |
| `DIGEST_MISMATCH` | The SHA-256 body digest does not match `Content-Digest`, or that header does not contain a usable matching `sha-256` value.                                                                                                                                               | Verify the raw body before parsing. Disable body transformations in front of the verifier. A syntactically broken digest header can instead produce `MALFORMED`.                                                               |
| `EXPIRED`         | `expires` is before `created`; the signed window exceeds 300 seconds; or the current time is outside `created - 60` through `expires + 60` with the default clock tolerance.                                                                                              | Synchronize your server clock, remove a fixed test clock and avoid holding signed requests in queues. Make a fresh call through Assay.                                                                                         |
| `UNKNOWN_KEY`     | No usable Ed25519 public key matches `keyid`. This includes a key fetch failure when no matching cached key is available.                                                                                                                                                 | Use the default public key URL, `https://api.goassay.ai/.well-known/assay-request-keys.json`. Check outbound HTTPS, DNS and firewall rules. Remove fixture keys from live configuration.                                       |
| `BAD_SIGNATURE`   | The selected key cannot verify the signature over the received request.                                                                                                                                                                                                   | Preserve method, host, path, query, call ID and signed digest. Set the public authority if a proxy rewrites `Host`. Register the final URL so the call does not redirect.                                                      |
| `REPLAYED`        | The nonce was already accepted and is still in the replay store.                                                                                                                                                                                                          | Stop proxy retries that resend the same signature. Make a new call through Assay. Use a fresh store per independent fixture case, but retain it within a replay test sequence.                                                 |

Keys are cached for one hour. A missing key can trigger a refetch at most once per 60 seconds. Fetches time out after 5 seconds; after a failure the verifier pauses retries for 5 seconds. Restore connectivity and allow the next fetch rather than bypassing the check.

## Other failures

These are adapter or infrastructure errors, outside the eight signature reasons.

| Caller sees                                                                                              | Cause                                                                                                        | Fix                                                                                                                                |
| -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `413 {"error":"payload_too_large"}`                                                                      | Worker or middleware body cap exceeded. The default is 5 MiB (5,242,880 bytes), checked before verification. | Reduce the body. Middleware can configure its cap; the ready-made Worker uses a fixed cap.                                         |
| Express: `500`, `error: "assay_verify_misconfigured"`, message `mount assayVerify() before body parsers` | The body stream was already read or parsed.                                                                  | Mount the verifier first.                                                                                                          |
| Express: `400 {"error":"invalid_json"}`                                                                  | Signature passed, but a JSON body could not be parsed.                                                       | Send valid JSON. This is separate from signature verification.                                                                     |
| Go middleware: `400 {"error":"bad_request"}`                                                             | Reading the body failed.                                                                                     | Check the connection and request body stream.                                                                                      |
| Go middleware: `500 {"error":"assay_verify_failed"}`                                                     | The replay store returned an error.                                                                          | Restore the shared store; do not serve the request on error.                                                                       |
| Worker with `NONCE_STORE`: `503 {"error":"replay_store_unavailable"}`                                    | The shared nonce store failed or returned an unusable answer.                                                | Restore the Durable Object binding and store. The origin is not called.                                                            |
| Node or Python: server/framework error                                                                   | A custom replay store throws. The library does not convert this into a signature reason.                     | Restore the store and keep verification failures closed. The exact HTTP error response depends on your framework's error handling. |

## Check it worked

1. Fix the failing check at your gateway or service.
2. In Assay's **Connect & test** step, click **Run the test again** with valid tool input.
3. Confirm the signed call passes and review the authentication warnings.

| Warning                    | Meaning                                                                                                                      | Fix                                                                   |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| **Signature not enforced** | In signature mode or **Both**, the endpoint accepted the tester's unsigned probe.                                            | Cover the called route with verification and close any origin bypass. |
| **API key not enforced**   | In API key mode or **Both**, the endpoint accepted the probe without stored key headers.                                     | Require your key at the endpoint.                                     |
| **No authentication**      | With **Neither**, the normal call returned a successful HTTP status. No separate authentication probe is made for this mode. | Select and enforce an authentication method.                          |

These warnings do not block **Go live**. A passing test without a warning is useful evidence, but a probe that cannot run also produces no warning. Check the protected route and origin access yourself.

## Go further

* [API key or signature](/developers/list-your-api-get-started/api-key-or-signature.md).
* List your API (optional): [Or: publish your own API](https://docs.goassay.ai/list-your-api/publish-your-own-api).

## Next

* [Map your API to a tool contract](/developers/list-your-api-advanced/map-your-api.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.goassay.ai/developers/list-your-api-get-started/reject-reasons.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
