> ## Documentation Index
> Fetch the complete documentation index at: https://developers.vantageclaw.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Integration test

> Verify your integration is ready for production traffic.

Before pointing real platform traffic at your handler, work through this checklist. It mirrors the failure modes we see most often in early integrations.

## Authentication

<Steps>
  <Step title="Valid key returns 200 on /me">
    ```bash theme={null}
    curl https://app.vantageclaw.ai/api/v1/partner/me \
      -H "Authorization: Bearer vck_test_REPLACE_ME.your_secret_half_here"
    ```

    Returns your org slug, key ID, and scope list. If `401`, your token is malformed or revoked.
  </Step>

  <Step title="Missing Authorization returns 401">
    The same request without the header should be rejected. If it succeeds, you are not actually using the partner API namespace — check the URL is `/api/v1/partner/...`.
  </Step>

  <Step title="Scope list matches what you requested">
    The `scopes` field in the `/me` response should contain `identity:read` plus any scopes granted to your key (e.g. `webhooks:manage`). If a scope you expected is missing, ask your VantageClaw contact to re-issue the key.
  </Step>
</Steps>

## Webhook subscription

<Steps>
  <Step title="Subscription created with secret captured">
    The `POST /api/v1/partner/webhooks` response includes a `secret` field. Confirm it was stored in your secret manager **before** the response was discarded — there is no recovery path.
  </Step>

  <Step title="Subscription appears in list">
    `GET /api/v1/partner/webhooks` should return your subscription with `active: true`, `consecutive_failures: 0`, and `auto_disabled_at: null`. The `secret` is omitted from list responses, which is expected.
  </Step>

  <Step title="URL validation rejects internal hosts">
    Attempt to create a subscription with `http://localhost:8080`, `http://192.168.1.1/`, or `https://server.local`. All three should return `422` with a validation error. If any succeed, you are not hitting the partner API namespace.
  </Step>
</Steps>

## Signature verification

<Steps>
  <Step title="Valid test event is accepted">
    Trigger `POST /api/v1/partner/webhooks/{id}/test` with the correct secret in the body. Your handler should log a verified event and return `2xx`.
  </Step>

  <Step title="Tampered body is rejected">
    Manually craft a request to your handler with valid signature headers but a modified body byte. Your handler must return non-2xx. If it accepts the request, your verification is reading the wrong body bytes (likely a JSON re-serialization in your middleware).
  </Step>

  <Step title="Old timestamp is rejected">
    Manually craft a request with `X-VC-Timestamp` set to a value more than 5 minutes in the past, signed with that timestamp. Your handler must reject it. If it accepts, your replay-window check is not running.
  </Step>

  <Step title="Wrong secret is rejected">
    Sign a request with the wrong secret and send it to your handler. Must be rejected. If accepted, you're not actually verifying — check for a `return True` hidden in error-handling.
  </Step>
</Steps>

## Delivery semantics

<Steps>
  <Step title="Handler responds within 10 seconds">
    Time your handler under realistic load. The dispatcher times out at 10 seconds per attempt. If your handler does heavy work synchronously, refactor to acknowledge with `2xx` first, process asynchronously.
  </Step>

  <Step title="Handler is idempotent on event id">
    Fire the same test event twice with `--include-retries` (or by returning `500` once to force a real retry). The second delivery has the same `evt_<uuid>` in the body. Your handler should detect the duplicate and not double-process.
  </Step>

  <Step title="Failed deliveries appear in the failures audit">
    Deliberately return `500` from your handler. Within seconds, `GET /api/v1/partner/webhooks/{id}/failures` should show the failed attempt with `http_status: 500`. Confirm pagination works by listing with `?limit=10`.
  </Step>

  <Step title="Subscription survives a brief outage">
    Take your handler offline for a few minutes, fire a test event, bring the handler back online. The retry schedule (1m, 5m, 30m, 2h, 6h, 24h) should redeliver the event once your handler is healthy. Without bringing it back, after 20 consecutive failures the subscription auto-disables.
  </Step>
</Steps>

## Operational readiness

<Steps>
  <Step title="Secret rotation procedure tested">
    Practice the delete-and-recreate flow in a non-production subscription:

    1. Create a second subscription with the same URL.
    2. Update your handler to accept either secret.
    3. Verify traffic flows to the new subscription.
    4. Delete the old subscription.
    5. Update your handler to drop the old secret.
  </Step>

  <Step title="Failed-delivery alerting wired">
    The auto-disable threshold is 20 consecutive failures. If your subscription gets auto-disabled, the only way to learn is by polling `GET /webhooks` and checking `active`/`auto_disabled_at`, or by polling `GET /webhooks/{id}/failures`. Decide your alert cadence and wire it in before going live.
  </Step>

  <Step title="Secret stored in secret manager, not source code">
    `grep -r "whsec_" .` in your codebase should return zero matches. The secret lives in environment variables, a secret manager, or an encrypted config — never in committed source.
  </Step>

  <Step title="Clock sync is healthy">
    `timedatectl status` (Linux) or equivalent should show synchronized NTP. The signature timestamp window is ±5 minutes — clock drift beyond that breaks legitimate verification.
  </Step>
</Steps>

## When all checks pass

You are ready to receive real events as soon as the relevant event categories ship. Your handler will verify and process them with no code changes — every future event type uses the same envelope shape and signature algorithm as `vc.test.ping`.

If a check fails and you can't immediately diagnose, capture:

* The full headers of the failing request (with `X-VC-Signature` and `X-VC-Timestamp`)
* The exact body bytes you signed against (length in bytes is the most useful diagnostic)
* The `evt_<uuid>` from the response

and contact your VantageClaw representative with those three values.
