---
title: "Mint a demo session: sign in as a demo account (admins only)."
method: POST
path: "/admin/demo-accounts/{userId}/sessions"
tags: ["Admin"]
---

# Mint a demo session: sign in as a demo account (admins only).

`POST /admin/demo-accounts/{userId}/sessions`

## Request

- Base URL: `https://docs.sleeperhit.studio/api/v1`
- URL: `https://docs.sleeperhit.studio/api/v1/admin/demo-accounts/{userId}/sessions`
- Auth: HTTP bearer

A demo session is a real sign-in as the demo account (not an impersonation, so no banner shows), for 2 hours by default and at most 8; it can be revoked at once, never makes the account an admin, and its cookies are written to a file only its owner can read, never printed. Answers a Playwright `storageState` for `origin`: hand it to `sleeperhit capture record --storage-state <file>`. Not idempotent (each call is a new sign-in, never replayed from a store), so it takes no Idempotency-Key. 403 `demo_account_not_allowed` unless the account is on the allowlist, exists, is not banned and is not an admin; 403 `demo_session_credential_refused` through a connected app's OAuth grant. Audited. Scope: `account:write`. Free.

## Request body

- DemoSessionMintRequest
  - `ttlSeconds` integer

## Response `201`

The session, and its storage state (a secret).

- DemoSessionMint
  - `session` DemoSession, required
    - `id` string, required: The session id. Never its token.
    - `demoUserId` string, required
    - `mintedById` string, nullable, required: The admin who minted it.
    - `createdAt` string, date-time, required
    - `expiresAt` string, date-time, required: The hard end: never extended.
  - `origin` string, required: The site the session signs in to.
  - `storageState` object, required: A Playwright storage state. SECRET: write it to a file only its owner can read (mode 600), outside any repository; never print or log it.
    - `cookies` object[], required
    - `origins` object[], required

## Other responses

- `401`: Missing, malformed, revoked, or expired API key.
- `403`: API key is valid but lacks the scope, is blocked by IP allowlist, or the account is disabled.
- `429`: Rate limit for this API key + IP + route family was exceeded.
- `500`: The API failed unexpectedly. Safe to retry with the same Idempotency-Key.

---

[API](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api.md) · [All operations](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api/llms.txt) · [OpenAPI document](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api/revisions/4534f5d25540?raw)
