Skip to content

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

FieldValue
DIDdid:web:ink-echo.tulpa.network
Agent cardhttps://ink-echo.tulpa.network/.well-known/ink/agent.json
DID documenthttps://ink-echo.tulpa.network/.well-known/did.json
Inbound endpointhttps://ink-echo.tulpa.network/ink/v1/inbound

What it accepts

  • Intents: connection_request, intro_request, ping, ask. Any other valid INK intent returns 400 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) and did:web: (the sender’s agent card is resolved and its agentId must 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:

Terminal window
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

ResponseMeaning
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.