vault

Redeem an Invite Key

kygit-invite (design D1/D3/D9). Verifies auth_secret against the stored auth_hash (constant-time compare, kind-domain-separated — an invite secret never verifies a handoff hash) and, on success, atomically flips the recipient into an active org member at the invite's stored role, resolves the room fields (inviter, live_presences, catch-up cursor), and returns the sealed envelope for the recipient's own client to decrypt. ONE transaction, no durable lease — first writer wins, the SAME engine a handoff redeem runs. A row of the WRONG KIND at this route (e.g. a real handoff id) is the identical 403 INVITE_KEY_INVALID — never disclosed. A SAME-principal replay before expiry returns 200 deduplicated:true with the ORIGINAL sealed envelope. Auth: SIWX WALLET ONLY — a control-plane session, grant key, or service key is refused 403 INVITE_REDEEM_REQUIRES_WALLET. Tier-free: principalAuth resolves-or-provisions the caller's own org-of-one with no active tier required (the joiner's own cold-start tier purchase, if any, is a separate client-side step this route never blocks on). Never lifecycle-gated. Response carries Cache-Control: no-store.

post/vaults/v1/invites/{invite_id}/redeem

Request

  • Base URL: https://api.run402.com
  • URL: https://api.run402.com/vaults/v1/invites/{invite_id}/redeem
  • Auth: API key in header SIGN-IN-WITH-X

Path parameters

invite_idstring uuid required

Headers

SIGN-IN-WITH-Xstring required

SIWX auth header — base64-encoded JSON payload containing a signed CAIP-122 (EIP-4361) message. See https://docs.x402.org/extensions/sign-in-with-x.

Request body

auth_secretstring required

Base64url — the HKDF-derived auth_secret half of the parsed kgi1_ key (64 lowercase hex characters are also accepted, the same compatibility form the handoff redeem route accepts). Client-derived only; a malformed value simply never matches any stored hash (never a distinguishing error).

writer_acceptanceobject required

vault-multi-writer rev 47, REQUIRED (no observe mode). The redeemer's own double-signed acceptance of the mint-time writer_admission_grant: {statement: {domain:"r402s/v0/handoff-writer-accept/v1", handoff_id, auth_hash, writer_key_id, signing_pubkey, encryption_pubkey, encryption_fingerprint}, acceptance_signature, possession_signature}. The signature domain and the handoff_id field name are the frozen protocol spelling (kygit-invite design D11); handoff_id carries THIS invite's id. acceptance_signature verifies under the STORED grant's handoff_admission_pubkey; possession_signature verifies under the redeemer's OWN signing_pubkey — both over the identical preimage, genuinely independent proofs. A redeem under a DIFFERENT writer_key_id than an earlier successful redeem by the same principal refuses 409 INVITE_REDEEM_WRITER_KEY_MISMATCH.

Response

Claimed. On a same-principal replay before expiry, deduplicated is true and sealed_envelope is the ORIGINAL envelope (never a pretend re-redeem).

invite_idstring uuid required
kind'invite' required
deduplicatedboolean required
sealed_envelopestring byte required

Base64 ciphertext — decrypt client-side under the derived wrap_key. The gateway never sees the plaintext.

envelope_kindstring required
repo_idstring required
org_idstring uuid required
project_idstring nullable required
membersobject[] required

Every current member of the org, so the recipient sees who else is already here.

live_presencesobject[] required

The room's live presences at redeem time.

cursorstring nullable required

The room's catch-up cursor (opaque mcr_...) — so the joiner's first messages wait/messages list call needs no flags.

expires_atstring date-time required
writer_admission_grantobject required

vault-multi-writer rev 47. The ORIGINAL writer_admission_grant from the mint, echoed back byte-for-byte so the joiner's own client can cross-check it against the v2 envelope's writer_admission_grant_sha256 (a value the gateway cannot forge) and then complete an add_writer_key transition under its OWN key.

Example response

{
  "next_actions": [
    {
      "method": "POST",
      "path": "/tiers/v1/hobby",
      "auth": "x402"
    }
  ]
}

Changes

No recorded changes to this operation across all 1 revision of this API.