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

# Test Mode

> Build and verify your integration for free with a sandbox API key

# Test Mode

Test mode is a free sandbox for the credit-consuming video endpoints. A test-mode API key exercises the **exact same** request, validation, async lifecycle, and signed-webhook surface as a live key — but it never spends credits, never runs the real generation pipeline, and instead returns a watermarked sample video.

It lets you build and verify *your integration* — request shape, authentication, polling, webhook delivery, signature verification, and error handling — without spending anything.

<Info>
  Test mode is available on the two credit-consuming endpoints: [Create Narrated Slideshow](/api-reference/videos/create-narrated-slideshow) and [Create Living Property Video](/api-reference/videos/create-living-property). All other endpoints behave identically regardless of key type.
</Info>

## The Two-Phase Model

Like most developer platforms, VibePeak separates *integration testing* from *output evaluation*:

<Steps>
  <Step title="Phase 1 — Integration (free, test mode)">
    Use a `vpk_test_` key to wire up and harden your integration end to end: building requests, handling `202` responses, polling or receiving webhooks, verifying HMAC signatures, and handling success and failure paths. This is unlimited and costs nothing.
  </Step>

  <Step title="Phase 2 — Output quality (a few live credits)">
    Once your integration is solid, switch to a `vpk_live_` key and spend a small number of real credits to judge the actual video quality. Test mode never produces a real video, so output quality is always evaluated separately with live credits.
  </Step>
</Steps>

## Key Prefixes

Every API key carries a prefix that determines its mode:

| Prefix      | Mode | Behavior                                                                    |
| ----------- | ---- | --------------------------------------------------------------------------- |
| `vpk_live_` | Live | Charges credits, runs the real generation pipeline, returns your real video |
| `vpk_test_` | Test | Charges nothing, returns a watermarked sample, ignores real generation      |

A key's mode is fixed at creation. You obtain a test-mode key from your dashboard the same way as a live key — see [Authentication](/authentication).

## What Test Mode Validates

A test request runs through the **identical** front-half of the pipeline as a live request. Everything you need to harden your integration is exercised for real:

<CardGroup cols={2}>
  <Card title="Authentication" icon="key">
    Bearer token parsing, key validity, and plan checks behave exactly as in live mode.
  </Card>

  <Card title="Full input validation" icon="circle-check">
    Schema validation, script length and character rules, voice and avatar checks — all enforced.
  </Card>

  <Card title="SSRF protection" icon="shield">
    Image and webhook URLs are still rejected if they are non-HTTPS (webhooks) or point to private networks.
  </Card>

  <Card title="Async lifecycle" icon="arrows-rotate">
    `queued → processing → completed | failed`, plus the `Location` and `Retry-After` headers.
  </Card>

  <Card title="Signed webhooks" icon="webhook">
    The completion webhook is delivered and HMAC-signed with the same headers as live.
  </Card>

  <Card title="Error & failure paths" icon="triangle-exclamation">
    Simulate a failed task to exercise your error and retry handling.
  </Card>
</CardGroup>

## What Test Mode Does *Not* Validate

<Warning>
  Test mode never runs the real generation pipeline, so it cannot tell you anything about the **quality, content, or real timing** of your video. The returned clip is a fixed sample, not a render of your images or script. Always judge output quality in Phase 2 with a `vpk_live_` key.
</Warning>

Specifically, a test request does **not**:

* Charge credits or touch your credit balance
* Call any third-party AI provider or run the video renderer
* Count against your live concurrency limit
* Reflect real generation time (the synthetic delay is short and configurable, not representative)

## The `test_scenario` Parameter

Both credit-consuming endpoints accept an optional `test_scenario` field so you can drive the synthetic lifecycle down a specific path. It is **honored only for test-mode keys and silently ignored for live keys**, so it is safe to leave in shared request-building code.

| Value                 | Outcome                                                                                                         |
| --------------------- | --------------------------------------------------------------------------------------------------------------- |
| `success` *(default)* | Task completes after a short delay with a sample video                                                          |
| `fail`                | Task fails with a sanitized, public-safe error so you can exercise your error handling                          |
| `slow`                | Like `success`, but completes after a noticeably longer delay so you can test polling timeouts and slow-path UX |

```json Request body (excerpt) theme={null}
{
  "test_scenario": "fail"
}
```

When `test_scenario` is `fail`, the task finishes with status `failed` and this error:

```json Failed test task theme={null}
{
  "task_id": "task_abc123xyz",
  "status": "failed",
  "livemode": false,
  "error": {
    "code": "TEST_MODE_SIMULATED_FAILURE",
    "message": "Simulated failure for test mode. No video was generated and no credits were used."
  }
}
```

## The `livemode` Field

Every task-creation response and every task-status response includes a boolean `livemode` field. It is the single, reliable signal for whether a task is real:

* `livemode: true` — created with a `vpk_live_` key; real credits were spent and a real video is produced.
* `livemode: false` — created with a `vpk_test_` key; this is a sandbox task with a sample video.

The same `livemode` field is included in the webhook payload, so your webhook handler can branch on it (for example, skip downstream billing or storage for sandbox events).

## Sample Asset Behavior

A successful test task (`success` or `slow`) completes with a fixed, watermarked sample:

```json Completed test task theme={null}
{
  "task_id": "task_abc123xyz",
  "status": "completed",
  "livemode": false,
  "result": {
    "video_url": "https://storage.vibepeak.ai/samples/sample-landscape.mp4",
    "cover_image_url": "https://storage.vibepeak.ai/samples/sample-cover.jpg",
    "duration_seconds": 12,
    "total_scenes": 3
  }
}
```

* The clip is **visibly watermarked `SAMPLE`** and is the same for every test request — it is *not* a render of your images or script.
* The sample is orientation-aware: a `portrait` request returns a portrait sample, otherwise a landscape one.
* `duration_seconds` and `total_scenes` are fixed sample metadata, not a prediction of your real video.

## Test / Live Isolation

Test and live traffic are fully isolated:

<Note>
  A `vpk_test_` key can only read test tasks, and a `vpk_live_` key can only read live tasks. Reading a task created with the other mode returns the same `404 TASK_NOT_FOUND` as a task that does not exist — there is no cross-mode existence leak.
</Note>

* Sandbox tasks never consume your live concurrency limit, and live tasks are never throttled by sandbox traffic.
* Sandbox tasks are excluded from usage and concurrency aggregation entirely.

## Recommended Workflow

<Steps>
  <Step title="Wire up with a test key">
    Build your request, submission, polling, and webhook handling against a `vpk_test_` key with `test_scenario: "success"`.
  </Step>

  <Step title="Exercise failure handling">
    Send `test_scenario: "fail"` and confirm your code handles a `failed` task and surfaces the error correctly.
  </Step>

  <Step title="Test slow paths">
    Send `test_scenario: "slow"` and confirm your polling/timeout logic and any loading UI behave well on a longer-running task.
  </Step>

  <Step title="Verify webhook signatures">
    Point `webhook_url` at your endpoint and confirm you verify the HMAC signature and branch on `livemode`.
  </Step>

  <Step title="Switch to live for quality">
    Swap to your `vpk_live_` key and spend a few real credits to evaluate actual video quality.
  </Step>
</Steps>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.vibepeak.ai/v1/real-estate/narrated-slideshow \
    -H "Authorization: Bearer vpk_test_xxxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "images": [
        "https://example.com/property/living-room.jpg",
        "https://example.com/property/kitchen.jpg",
        "https://example.com/property/bedroom.jpg",
        "https://example.com/property/bathroom.jpg",
        "https://example.com/property/backyard.jpg",
        "https://example.com/property/exterior.jpg"
      ],
      "voice": { "voice_id": "EXAVITQu4vr4xnSDxMaL", "language": "en" },
      "script": "Welcome to this stunning home in the heart of the city. The open living area floods with natural light and flows into a modern kitchen...",
      "test_scenario": "success",
      "webhook_url": "https://yourserver.com/webhooks/vibepeak"
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch('https://api.vibepeak.ai/v1/real-estate/narrated-slideshow', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer vpk_test_xxxxx',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      images: [
        'https://example.com/property/living-room.jpg',
        'https://example.com/property/kitchen.jpg',
        'https://example.com/property/bedroom.jpg',
        'https://example.com/property/bathroom.jpg',
        'https://example.com/property/backyard.jpg',
        'https://example.com/property/exterior.jpg'
      ],
      voice: { voice_id: 'EXAVITQu4vr4xnSDxMaL', language: 'en' },
      script: 'Welcome to this stunning home in the heart of the city...',
      test_scenario: 'success',
      webhook_url: 'https://yourserver.com/webhooks/vibepeak'
    })
  });

  const task = await response.json();
  console.log(`Test task created: ${task.task_id} (livemode: ${task.livemode})`);
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://api.vibepeak.ai/v1/real-estate/narrated-slideshow',
      headers={
          'Authorization': 'Bearer vpk_test_xxxxx',
          'Content-Type': 'application/json'
      },
      json={
          'images': [
              'https://example.com/property/living-room.jpg',
              'https://example.com/property/kitchen.jpg',
              'https://example.com/property/bedroom.jpg',
              'https://example.com/property/bathroom.jpg',
              'https://example.com/property/backyard.jpg',
              'https://example.com/property/exterior.jpg'
          ],
          'voice': {'voice_id': 'EXAVITQu4vr4xnSDxMaL', 'language': 'en'},
          'script': 'Welcome to this stunning home in the heart of the city...',
          'test_scenario': 'success',
          'webhook_url': 'https://yourserver.com/webhooks/vibepeak'
      }
  )

  task = response.json()
  print(f"Test task created: {task['task_id']} (livemode: {task['livemode']})")
  ```
</CodeGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Authentication" icon="key" href="/authentication">
    Key prefixes, formats, and how to obtain them
  </Card>

  <Card title="Async Processing" icon="arrows-rotate" href="/concepts/async-processing">
    The task lifecycle and polling strategy
  </Card>

  <Card title="Webhooks" icon="webhook" href="/concepts/webhooks">
    Verifying signatures and handling `livemode`
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="/concepts/error-handling">
    Error codes and retry strategies
  </Card>
</CardGroup>
