---
title: "Complete a Nostr identity link with a dual-signature proof"
method: POST
path: "/identity-links/v1"
tags: ["Identity Links"]
---

# Complete a Nostr identity link with a dual-signature proof

`POST /identity-links/v1`

## Request

- Base URL: `https://api.run402.com`
- URL: `https://api.run402.com/identity-links/v1`
- Auth: API key in header `SIGN-IN-WITH-X`

Requires the exact agent principal and its own SIWX EOA that created the challenge (never a grant key). The request body is parsed from preserved UTF-8 bytes with duplicate detection. The seven-field kind-1 event must contain the canonical two-field wrapper and either no tags or exactly one independently valid empty-condition NIP-OA owner-attestation tag. The Nostr link is public attribution only and is never authentication, authorization, ownership, payment authority, or a transfer target. Limits: five failed completions per challenge, 20 completions per 10 minutes per principal, and 60 per 10 minutes per IP.

## Headers

- `SIGN-IN-WITH-X` string, required

## Request body

- IdentityLinkCompletionRequest
  - `identity_link_challenge_id` string, required
  - `nostr_event` NostrIdentityProofEvent, required
    - `id` string, required
    - `pubkey` string, required
    - `created_at` integer, required: Exact signed NIP-01 event time in Unix seconds. This protocol envelope field is the only identity-link exception to the ISO-8601 HTTP timestamp contract; converting it would invalidate the event id and signature.
    - `kind` 1 | 24243, required
    - `tags` array[], required
      - string[]: Protocol-discriminated public proof tags: optional NIP-OA for agent kind-1 proofs, or exact Run402 challenge-binding tags for human kind-24243 proofs.
    - `content` string, required: Exact RFC 8785 serialization of {public_payload,wallet_signature}.
    - `sig` string, required

## Response `200`

Exact event-id retry

- IdentityLinkProof
  - `identity_link_id` string, required
  - `proof_protocol` 'run402.identity-link.nostr.v1' | 'run402.identity-link.nostr.human.v1', required
  - `status` 'active' | 'revoked', required
  - `effective_status` 'active' | 'revoked' | 'principal_inactive', required
  - `verified_at` string, date-time, required
  - `revoked_at` string, date-time, nullable, required
  - `public_subject` string, required
  - `display_subject` string, required
  - `proved_principal` IdentityLinkPrincipalAttribution, required
    - `principal_id` string, required
    - `principal_type` string, required
  - `effective_principal` IdentityLinkPrincipalAttribution, required
    - `principal_id` string, required
    - `principal_type` string, required
  - `public_payload` string, nullable, required
  - `wallet_signature` string, nullable, required
  - `nostr_event` NostrIdentityProofEvent, required
    - `id` string, required
    - `pubkey` string, required
    - `created_at` integer, required: Exact signed NIP-01 event time in Unix seconds. This protocol envelope field is the only identity-link exception to the ISO-8601 HTTP timestamp contract; converting it would invalidate the event id and signature.
    - `kind` 1 | 24243, required
    - `tags` array[], required
      - string[]: Protocol-discriminated public proof tags: optional NIP-OA for agent kind-1 proofs, or exact Run402 challenge-binding tags for human kind-24243 proofs.
    - `content` string, required: Exact RFC 8785 serialization of {public_payload,wallet_signature}.
    - `sig` string, required
  - `verification_statement` HumanIdentityLinkVerificationStatement, required
    - `schema_version` 1, required
    - `acceptance_context` 'standalone_human_link' | 'buzz_human_adoption', required
    - `adopting_principal` 'run402_verified', required
    - `target_session` 'run402_verified' | 'legacy_run402_verified', required
    - `fresh_passkey` 'run402_verified', required
    - `verified_at` string, date-time, required

## Other responses

- `201`: Identity link created
- `400`: Malformed outer JSON, duplicate/unknown field, or forbidden private-key-shaped input
- `403`: Exact principal/authenticator changed
- `409`: Challenge expired, replay, or uniqueness conflict
- `422`: Incomplete/noncanonical/tag-disallowed event or invalid dual proof
- `429`: IDENTITY_LINK_RATE_LIMITED. Retry-After and details.retry_after_seconds are present.
- `503`: Pre-launch or incident completion shutdown (IDENTITY_LINK_CAPABILITY_UNAVAILABLE)

---

[API](https://skmtc.dev/run402/apis/run402-api.md) · [All operations](https://skmtc.dev/run402/apis/run402-api/llms.txt) · [OpenAPI document](https://skmtc.dev/run402/apis/run402-api/revisions/893607557f50?raw)
