Public Test Endpoint
ink-echo.tulpa.network is a public INK receiver you can send signed envelopes
to while building or debugging a sender. It runs the open-source
examples/reference-receiver,
so its whole behaviour is auditable — there is no hidden server logic.
It accepts an envelope, verifies the signature against the resolved sender
signing keys, and returns a JSON acknowledgement. A 200 means your bytes are
on the canonical wire. A 400 tells you which check failed.
Identity
| Field | Value |
|---|---|
| DID | did:web:ink-echo.tulpa.network |
| Agent card | https://ink-echo.tulpa.network/.well-known/ink/agent.json |
| DID document | https://ink-echo.tulpa.network/.well-known/did.json |
| Inbound endpoint | https://ink-echo.tulpa.network/ink/v1/inbound |
What it accepts
- Intents:
connection_request,intro_request,ping,ask. Any other valid INK intent returns400 unsupported_intent; a string that is not an INK intent at all fails schema validation first (400 schema:…). - Sender DID methods:
did:key:(the key is decoded inline from the identifier) anddid:web:(the sender’s agent card is resolved and itsagentIdmust match the DID). Other methods are rejected as unresolvable. - Authentication: the INK transport signature (
Authorization: INK-Ed25519 <sig>) per the Authentication spec, with timestamp freshness and single-use nonce enforcement.
Send one with the interop CLI
The interop-cli
signs from a did:key:, which this receiver decodes without a network lookup:
pip install -e . # from examples/interop-cli/ink-interop keygen --out-seed /tmp/sender.seed # prints your did:key
ink-interop send \ --seed /tmp/sender.seed \ --from-did did:key:<printed-multibase> \ --to-did did:web:ink-echo.tulpa.network \ --target-url https://ink-echo.tulpa.network/ink/v1/inbound \ --path /ink/v1/inbound \ --intent-type connection_request \ --purpose "interop smoke"The CLI prints a wrapper with the request it sent and the response it got
back. On success the receiver’s HTTP response body (under response.json) is:
{ "ok": true, "receiverDid": "did:web:ink-echo.tulpa.network", "receivedAt": "2026-06-02T18:24:17.061Z", "receivedIntent": "connection_request", "inReplyTo": "01KT…", "correlationId": "01KT…"}The acknowledgement is plain JSON — this reference receiver does not sign its
response. Correlate it to your envelope with inReplyTo.
Reading the result
| Response | Meaning |
|---|---|
200 { "ok": true, … } | Envelope accepted. Your signature, freshness, and schema are all correct. |
400 { "code": "auth:signature_verification_failed" } | The transport signature did not verify. Usually a signature-base mismatch — check the method, path, recipient DID, JCS body, and timestamp. |
400 { "code": "auth:timestamp_expired" } (and other auth:…) | The transport-auth check from verifyInkAuth failed for a non-signature reason — stale timestamp, missing or replayed nonce, missing or malformed Authorization header. |
400 { "code": "sender_key_unresolved" } | The receiver could not resolve a signing key for your from DID. |
400 { "code": "unsupported_intent:…" } | The intent is not in the accepted set above. |
400 { "code": "recipient_mismatch" } | The envelope to is not did:web:ink-echo.tulpa.network. |
400 { "code": "from_field_mismatch" } | The authenticated sender did not match the envelope from. |
400 { "error": "schema", "code": "json_parse_failed" | "schema:…" } | The body was not valid JSON, or failed validateMessage. |
413 { "error": "oversize" } | The request body exceeded the 64 KB cap. |
415 { "error": "unsupported_content_type" } | The request was not application/json. |
429 { "error": "rate_limited" } | Per-IP or per-sender rate limit hit. Back off and retry. |
This table is representative, not exhaustive; the auth:… family mirrors the
error codes returned by the protocol’s transport-auth
middleware.
Caveats
Treat the deployment as a moving target. Keys rotate, rate limits drop unauthenticated callers, and it may go offline without notice. It is a test aid, not a dependency — the canonical artifact is the source, not the live URL. For a target you fully control, run that example locally.