# 10-minute quick start

> Create a test session, complete it with sandbox values and receive the webhook.

Source: https://docs.okyc.stream/get-started/quickstart

You need a dashboard account, a published workflow (the default **KYC** workflow is fine) and a test key.
No account yet? [Request access](https://dashboard.okyc.stream/sign-up) — once your request is approved, you get an
email with a link to create your account, and you start in test mode.

    ### Get a test key and a workflow id

    In the dashboard open **Developers → API keys** and create a key in **test** mode (`sk_test_…`). Copy the id of
    a published workflow from **Workflows** (`wf_…`).

    ### Create a session

    ```bash
    curl https://api.okyc.stream/v1/sessions \
      -H "Authorization: Bearer $OKYC_SECRET_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: signup-user_123" \
      -d '{ "workflow_id": "wf_…", "reference_id": "user_123" }'
    ```

    The response contains the session `id`, its `status` (`created`), the hosted flow `url`, a `client_token` and
    `expires_at`. With Node, the same call through `@okyc/node`:

    ```js title="server.mjs"
    // Your frontend calls this, then passes { clientToken, sessionUrl } to @okyc/web or @okyc/react.
    app.post('/api/kyc/session', express.json(), async (req, res) => {
      const userId = req.header('x-user-id') ?? 'demo-user'; // use your own auth here
      try {
        const session = await okyc.sessions.create(
          {
            workflowId: process.env.OKYC_WORKFLOW_ID,
            referenceId: userId,
            redirectUrl: process.env.REDIRECT_URL,
          },
          { idempotencyKey: `kyc-${userId}-${new Date().toISOString().slice(0, 10)}` },
        );
        res.json({ clientToken: session.clientToken, sessionUrl: session.url });
      } catch (e) {
        res.status(e.statusCode ?? 500).json({ error: e.code ?? 'error' });
      }
    });
    ```

    ### Open the flow

    Paste the `url` in a browser (or pass it to a [web](https://docs.okyc.stream/web/web-sdk.md) or [mobile](https://docs.okyc.stream/mobile.md) SDK). Complete it with
    [test data](https://docs.okyc.stream/get-started/sandbox.md#test-data-in-the-hosted-flow): every step offers **Use test data**, and the OTP
    is `123456`.

    ### Receive the webhook

    Add an endpoint under **Developers → Webhooks** (test mode) and copy its signing secret (`whsec_…`). For local
    development, expose your server with a tunnel. Verify every delivery:

    ```js title="server.mjs"
    // Webhooks: verify against the RAW body, dedupe on event.id, then fetch details from the API.
    const seen = new Set();
    app.post('/okyc/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
      let event;
      try {
        event = await okyc.webhooks.constructEvent(
          req.body,
          req.header('okyc-signature'),
          process.env.OKYC_WEBHOOK_SECRET,
        );
      } catch {
        return res.sendStatus(400);
      }
      if (seen.has(event.id)) return res.sendStatus(200);
      seen.add(event.id);
      if (event.type === 'session.approved' || event.type === 'session.declined') {
        const session = await okyc.sessions.retrieve(event.data.session.id);
        console.log(`user ${session.reference_id} → ${session.status}`);
        // mark the user verified / rejected in your database
      }
      res.sendStatus(200);
    });
    ```

    A passing test session sends `session.created`, `session.started`, `session.processing` and finally
    `session.approved` (to endpoints subscribed to those events).

    ### Fetch the result

    ```bash
    curl https://api.okyc.stream/v1/sessions/ses_…?expand=checks \
      -H "Authorization: Bearer $OKYC_SECRET_KEY"
    ```

Next: [sessions](https://docs.okyc.stream/backend/sessions.md), [webhooks](https://docs.okyc.stream/backend/webhooks.md), or a [mobile SDK](https://docs.okyc.stream/mobile.md).
