Skip to main content
When a developer approves signup, Gate posts a gate.session.approved event to your webhook endpoint. Unlike other Foil webhooks, this one has a request/response contract: your handler must return an encrypted credentials envelope that the CLI decrypts and writes into the developer’s project. This page covers the Gate-specific bits. For everything common to all Foil webhooks — signature verification, retries, headers, the delivery log — see Webhooks.

Set up the endpoint

Create a webhook endpoint subscribed to gate.session.approved, then point your Gate service at it via webhook_endpoint_id. See Webhooks → Endpoints for both the dashboard flow and the API.

Verify the signature

Standard Foil HMAC verification — see Webhooks → Authentication for ready-to-paste code in Node.js, Python, Go, Ruby, and PHP.

Handle the payload

The envelope is the standard webhook envelope. The gate.session.approved data object is:
{
  "service_id": "foil",
  "gate_session_id": "gate_abc123...",
  "gate_account_id": "gacct_abc123...",
  "account_name": "my-project",
  "metadata": null,
  "delivery": {
    "version": 1,
    "algorithm": "x25519-hkdf-sha256/aes-256-gcm",
    "key_id": "<base64url sha256 of public_key>",
    "public_key": "<base64url raw X25519 public key>"
  },
  "foil": {
    "verdict": "human",
    "score": 0.12
  }
}
FieldDescription
service_idThe registry service being provisioned. Use it if one webhook handles multiple services.
gate_session_idIdempotency key. Use this to avoid creating duplicate accounts on retries.
gate_account_idStable identifier for this signup. Store it alongside your internal account.
account_nameThe name the developer chose (defaults to their project directory name).
metadataOptional key-value pairs the developer passed during signup.
deliveryCLI-provided encrypted delivery metadata. Encrypt your response outputs to this key.
foil.verdicthuman or inconclusive. Bot verdicts are blocked before reaching your webhook.
The full schema is also published in the API reference under Webhooks.

Return credentials

Your webhook must return an encrypted delivery envelope, not raw keys or onboarding links. Gate owns the service metadata like docs_url; your webhook should only return the encrypted customer outputs.
{
  "encrypted_delivery": {
    "version": 1,
    "algorithm": "x25519-hkdf-sha256/aes-256-gcm",
    "key_id": "<same key_id>",
    "ephemeral_public_key": "<base64url raw X25519 public key>",
    "salt": "<base64url 32 bytes>",
    "iv": "<base64url 12 bytes>",
    "ciphertext": "<base64url>",
    "tag": "<base64url 16 bytes>"
  }
}
When decrypted by the CLI, the ciphertext should contain your registry-owned env_vars only, for example:
{
  "version": 1,
  "outputs": {
    "YOURPRODUCT_PUBLISHABLE_KEY": "pk_live_...",
    "YOURPRODUCT_SECRET_KEY": "sk_live_..."
  }
}

Idempotency

Gate may retry your webhook. Use gate_session_id to make requests idempotent:
// Check if we already processed this session
const existing = await db.findByGateSessionId(gate_session_id);
if (existing) return res.json(existing.credentials);

// First time — create the account
const account = await createAccount(account_name);
// ... save gate_session_id for future retries
The general retry rules — 5 attempts, ≥ 1 minute between attempts, dedupe by envelope id — apply here too. See Webhooks → Retries.

What’s next