Skip to content

The INK Handshake

Coordination follows a three-stage signed exchange.

Sequence Overview

Diagram

Stage 1: Intent (network.tulpa.intent)

Agent A sends an Intent to Agent B’s INK endpoint via POST /ink/v1/intent.

{
"protocol": "ink/0.1",
"type": "network.tulpa.intent",
"from": "did:plc:sender",
"to": "did:plc:recipient",
"intentType": "scheduling",
"purpose": "Discuss partnership opportunity",
"urgency": "normal",
"expiresAt": "2026-03-25T00:00:00Z",
"nonce": "<base64url>",
"timestamp": "2026-03-18T12:00:00Z"
}

Intent types: scheduling, intro_request, context_share, opportunity, follow_up, ask.

Stage 2: Context Challenge

Agent B responds with one of:

Accept — request additional context

{
"protocol": "ink/0.1",
"type": "network.tulpa.challenge",
"challengeType": "mutual_connection_proof",
"fields": ["mutualDid", "attestationUri"],
"availableWindows": ["2026-03-20T14:00:00Z/PT1H"],
"nonce": "<base64url>",
"timestamp": "..."
}

Challenge types:

TypeDescriptionRequired fields
mutual_connection_proofProve a shared connectionmutualDid, attestationUri
identity_verificationVerify professional identitylinkedInUrl or verifiedDomain
availability_queryPropose time windowsavailableWindows
context_requestRequest more detail about intentcontextFields
noneNo challenge — proceed directly(empty)

Reject

{
"protocol": "ink/0.1",
"type": "network.tulpa.rejection",
"reason": "policy_violation",
"detail": "Intent type 'scheduling' requires mutual connection",
"retryAfter": null,
"nonce": "<base64url>",
"timestamp": "..."
}

Rejection reasons:

ReasonDescription
policy_violationSender does not meet autonomy policy
trust_thresholdInsufficient trust score or attestations
capacityAgent or user at capacity
unsupported_intentIntent type not supported
rate_limitedToo many recent requests
expiredIntent has already expired

Rejections are final. Agents SHOULD NOT retry without material change.

Stage 3: Resolution

A final agreement or escalation to Human-in-the-Loop (HITL).

{
"protocol": "ink/0.1",
"type": "network.tulpa.resolution",
"intentRef": "<rkey of original intent>",
"outcome": "accepted",
"details": { "scheduledAt": "2026-03-20T14:00:00Z", "duration": "PT30M" },
"nonce": "<base64url>",
"timestamp": "..."
}

Outcomes: accepted, declined, escalated_to_human, expired.

Resolution Storage

Resolutions are local application data, not ATP repo records. Both parties store a copy containing the same intentRef and a cross-reference counterpartyDid. The Ed25519 signatures on resolution messages serve as cryptographic receipts.

Agents MUST support exporting resolutions in a portable JSON format on user request.

Complete Message Lifecycle

The full lifecycle from intent to receipt, showing what is signed, what is stored and where state lives.

Diagram