Skip to content

API reference

Account and API keys API

Check API health, read the account and available generation balance behind an API key, and create, list, or revoke scoped API keys.

BetaUpdated

Base URL
https://lyricwinter.com/api/v1
Authentication
Authorization: Bearer lw_…
Contract
openapi.json

Check API health

GET/health

No authentication

Returns a static payload when the LyricWinter API is reachable. It needs no authentication and performs no account checks.

Response 200

Stable API health response. Fields below are inside data.

  • oktruerequired
  • service"lyricwinter"required
  • version"v1"required
curl "https://lyricwinter.com/api/v1/health" \
  -H "Authorization: Bearer $LYRICWINTER_API_KEY"
Response 200 (example)
{
  "data": {
    "ok": true,
    "service": "lyricwinter",
    "version": "v1"
  },
  "request_id": "req_01J9Z3K8QF4"
}

Get the current account

GET/me

Scope account:read

Returns the profile of the account that owns the API key: its ID, username, display name, avatar URL, and email.

Response 200

Current account profile. Fields below are inside data.

  • profileobjectrequired
    5 child attributes
    • idstringrequired
    • usernamestring | nullrequired3–30 chars
    • namestring | nullrequiredmax 100 chars
    • imagestring | nullrequireduri
    • emailstring | nullrequiredemail

Errors 401, 403, 500 use the standard error envelope.

curl "https://lyricwinter.com/api/v1/me" \
  -H "Authorization: Bearer $LYRICWINTER_API_KEY"
Response 200 (example)
{
  "data": {
    "profile": {
      "id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
      "username": "string",
      "name": "My integration",
      "image": "https://lyricwinter.com/…",
      "email": "[email protected]"
    }
  },
  "request_id": "req_01J9Z3K8QF4"
}

Read generation preferences

GET/me/generation-preferences

Scope account:read

Read the account's saved Allow fallbacks setting. New accounts start with it enabled. The preference is shared across devices.

Response 200

Saved generation preference. Fields below are inside data.

  • allow_fallbacksbooleanrequired

Errors 401, 403, 500 use the standard error envelope.

curl "https://lyricwinter.com/api/v1/me/generation-preferences" \
  -H "Authorization: Bearer $LYRICWINTER_API_KEY"
Response 200 (example)
{
  "data": {
    "allow_fallbacks": true
  },
  "request_id": "req_01J9Z3K8QF4"
}

Save generation preferences

PATCH/me/generation-preferences

Scope account:write

Save Allow fallbacks by sending allow_fallbacks as true or false. The response contains the saved value. Changing this preference does not alter work already submitted.

Request body application/json

  • allow_fallbacksbooleanrequired

Response 200

Saved generation preference. Fields below are inside data.

  • allow_fallbacksbooleanrequired

Errors 400, 401, 403, 500 use the standard error envelope.

curl -X PATCH "https://lyricwinter.com/api/v1/me/generation-preferences" \
  -H "Authorization: Bearer $LYRICWINTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "allow_fallbacks": true
}'
Response 200 (example)
{
  "data": {
    "allow_fallbacks": true
  },
  "request_id": "req_01J9Z3K8QF4"
}

Get the current account balance

GET/me/balance

Scope account:read

Returns the caller's current account generation balance with account:read. data.balance.snowcoins is settled; data.balance.available_snowcoins is available after outstanding holds. For promotional credits and the combined Studio balance, use /studio/account-capabilities with studio:read. These values are current snapshots, not historical balances for billing activity. The server still reserves and charges credits atomically. Subscription state and usage history are separate resources.

Response 200

Cached balance and subscription-adjacent state for the authenticated caller. Fields below are inside data.

  • balanceobjectrequired
    13 child attributes
    • snowcoinsinteger | nullrequired≥ 0

      Current settled account Snowcoins.

    • available_snowcoinsinteger | nullrequired≥ 0

      Current spendable account Snowcoins after active holds.

    • billing_debtintegerrequired≥ 0
    • subscription_tierstringrequired

      One ofzerowintermywinterultrawinterhyperwinter

    • subscription_providerstring | nullrequired

      One ofstripeapp_store

    • subscription_statusstringrequired

      One offreeactivegrace_periodbilling_retryexpiredrevoked

    • has_active_subscriptionbooleanrequired
    • parse_limit_charactersintegerrequired≥ 1
    • unlimited_f5_ttsbooleanrequired
    • auto_reload_enabledbooleanrequired
    • auto_reload_thresholdnumber | nullrequired
    • small_word_packs_eligiblebooleanrequired
    • small_word_pack_eligibilityobjectrequired
      7 child attributes
      • canUseSmallWordPacksbooleanrequired
      • activeSubscriptionbooleanrequired
      • previousSuccessfulPaymentbooleanrequired
      • engagedReturningUserbooleanrequired
      • completedAudioRunsLast30Daysintegerrequired≥ 0
      • accountAgeDaysinteger | nullrequired≥ 0
      • accountCreatedAtstring | nullrequireddate-time

Errors 401, 403, 500 use the standard error envelope.

curl "https://lyricwinter.com/api/v1/me/balance" \
  -H "Authorization: Bearer $LYRICWINTER_API_KEY"
Response 200 (example)
{
  "data": {
    "balance": {
      "snowcoins": 1,
      "available_snowcoins": 1,
      "billing_debt": 1,
      "subscription_tier": "zerowinter",
      "subscription_provider": "stripe",
      "subscription_status": "free",
      "has_active_subscription": true,
      "parse_limit_characters": 1,
      "unlimited_f5_tts": true,
      "auto_reload_enabled": true,
      "auto_reload_threshold": 1,
      "small_word_packs_eligible": true,
      "small_word_pack_eligibility": {
        "canUseSmallWordPacks": true,
        "activeSubscription": true,
        "previousSuccessfulPayment": true,
        "engagedReturningUser": true,
        "completedAudioRunsLast30Days": 1,
        "accountAgeDays": 1,
        "accountCreatedAt": "2026-09-30T17:00:00.000Z"
      }
    }
  },
  "request_id": "req_01J9Z3K8QF4"
}

Get available Studio funding and actions

GET/studio/account-capabilities

Scope studio:read

Returns the caller's account balance, promotional Studio credits, plan, and action capabilities with studio:read. data.studio_funding.total_available_snowcoins and total_available_words include both account and promotional funding after holds. as_of identifies the snapshot. Actions are advisory, not a live provider-health check or a guarantee that a later paid request will succeed. Reading this endpoint can claim an eligible account reward before returning the snapshot; repeated reads do not grant the same reward twice.

Response 200

Current Studio account capabilities from one consistent billing snapshot. Fields below are inside data.

  • schema_version"studio-account-capabilities-4"required
  • as_ofstringrequireddate-time
  • planobjectrequired
    3 child attributes
    • tierstringrequired

      One ofzerowintermywinterultrawinterhyperwinter

    • display_namestringrequired
    • character_limitintegerrequired≥ 1
  • balanceobjectrequired
    6 child attributes
    • settled_snowcoinsintegerrequired≥ 0
    • held_snowcoinsintegerrequired≥ 0
    • available_snowcoinsintegerrequired≥ 0
    • settled_wordsintegerrequired≥ 0
    • held_wordsintegerrequired≥ 0
    • available_wordsintegerrequired≥ 0
  • studio_fundingobjectrequired
    5 child attributes
    • promotionalobjectrequired
      5 child attributes
      • available_snowcoinsintegerrequired≥ 0
      • held_snowcoinsintegerrequired≥ 0
      • available_wordsintegerrequired≥ 0
      • held_wordsintegerrequired≥ 0
      • grantsarray of objectrequired
        7 item attributes
        • campaign_keystringrequired
        • display_namestringrequired
        • available_snowcoinsintegerrequired≥ 0
        • held_snowcoinsintegerrequired≥ 0
        • available_wordsintegerrequired≥ 0
        • held_wordsintegerrequired≥ 0
        • expires_atstring | nullrequireddate-time
    • total_available_snowcoinsintegerrequired≥ 0
    • total_held_snowcoinsintegerrequired≥ 0
    • total_available_wordsintegerrequired≥ 0
    • total_held_wordsintegerrequired≥ 0
  • campaign_dialogsarray of objectrequiredmax 20 items
    12 item attributes
    • campaign_keystringrequiredpattern ^[a-z0-9][a-z0-9_]{2,63}$
    • campaign_kindstringrequired

      One ofpromotioncustomer_referralaffiliateannouncementproduct_update

    • priorityintegerrequired-1000–1000
    • content_revisionintegerrequired≥ 1
    • interactionstringrequired

      One ofclaimacknowledge

    • titlestringrequired1–120 chars
    • bodystringrequired1–1000 chars
    • primary_action_labelstringrequired1–80 chars
    • secondary_action_labelstring | nullrequired1–80 chars
    • learn_more_urlstring | nullrequireduripattern ^https://
    • available_untilstringrequireddate-time
    • rewardsarray of objectrequiredmax 20 items
      7 item attributes
      • reward_keystringrequired
      • reward_type"studio_words"required
      • amount_snowcoinsintegerrequired≥ 1
      • amount_wordsintegerrequired≥ 1
      • expiration_policystringrequired

        One ofnonefixedrolling

      • fixed_expires_atstring | nullrequireddate-time
      • expires_in_secondsinteger | nullrequired≥ 1
  • campaign_offersarray of objectrequiredmax 20 items
    12 item attributes
    • campaign_keystringrequiredpattern ^[a-z0-9][a-z0-9_]{2,63}$
    • campaign_kindstringrequired

      One ofpromotioncustomer_referralaffiliateannouncementproduct_update

    • priorityintegerrequired-1000–1000
    • content_revisionintegerrequired≥ 1
    • interactionstringrequired

      One ofclaimacknowledge

    • titlestringrequired1–120 chars
    • bodystringrequired1–1000 chars
    • primary_action_labelstringrequired1–80 chars
    • secondary_action_labelstring | nullrequired1–80 chars
    • learn_more_urlstring | nullrequireduripattern ^https://
    • available_untilstringrequireddate-time
    • rewardsarray of objectrequiredmax 20 items
      7 item attributes
      • reward_keystringrequired
      • reward_type"studio_words"required
      • amount_snowcoinsintegerrequired≥ 1
      • amount_wordsintegerrequired≥ 1
      • expiration_policystringrequired

        One ofnonefixedrolling

      • fixed_expires_atstring | nullrequireddate-time
      • expires_in_secondsinteger | nullrequired≥ 1
  • purchasesobjectrequired
    1 child attributes
    • small_word_packs_eligiblebooleanrequired
  • actionsobjectrequired
    6 child attributes
    • prepare_textobjectrequired
      2 child attributes
      • statusstringrequired

        One ofavailablenot_connected

      • billing_modestringrequired

        One ofincludedmetered_snowcoinsnot_applicable

    • speech_generationobjectrequired
      2 child attributes
      • statusstringrequired

        One ofavailablenot_connected

      • billing_modestringrequired

        One ofincludedmetered_snowcoinsnot_applicable

    • sfx_generationobjectrequired
      2 child attributes
      • statusstringrequired

        One ofavailablenot_connected

      • billing_modestringrequired

        One ofincludedmetered_snowcoinsnot_applicable

    • voice_designobjectrequired
      2 child attributes
      • statusstringrequired

        One ofavailablenot_connected

      • billing_modestringrequired

        One ofincludedmetered_snowcoinsnot_applicable

    • voice_cloningobjectrequired
      2 child attributes
      • statusstringrequired

        One ofavailablenot_connected

      • billing_modestringrequired

        One ofincludedmetered_snowcoinsnot_applicable

    • final_renderobjectrequired
      2 child attributes
      • statusstringrequired

        One ofavailablenot_connected

      • billing_modestringrequired

        One ofincludedmetered_snowcoinsnot_applicable

Errors 401, 403, 500 use the standard error envelope.

curl "https://lyricwinter.com/api/v1/studio/account-capabilities" \
  -H "Authorization: Bearer $LYRICWINTER_API_KEY"
Response 200 (example)
{
  "data": {
    "schema_version": "studio-account-capabilities-4",
    "as_of": "2026-09-30T17:00:00.000Z",
    "plan": {
      "tier": "zerowinter",
      "display_name": "string",
      "character_limit": 1
    },
    "balance": {
      "settled_snowcoins": 1,
      "held_snowcoins": 1,
      "available_snowcoins": 1,
      "settled_words": 1,
      "held_words": 1,
      "available_words": 1
    },
    "studio_funding": {
      "promotional": {
        "available_snowcoins": 1,
        "held_snowcoins": 1,
        "available_words": 1,
        "held_words": 1,
        "grants": [
          {
            "campaign_key": "string",
            "display_name": "string",
            "available_snowcoins": 1,
            "held_snowcoins": 1,
            "available_words": 1,
            "held_words": 1,
            "expires_at": "2026-09-30T17:00:00.000Z"
          }
        ]
      },
      "total_available_snowcoins": 1,
      "total_held_snowcoins": 1,
      "total_available_words": 1,
      "total_held_words": 1
    },
    "campaign_dialogs": [
      {
        "campaign_key": "string",
        "campaign_kind": "promotion",
        "priority": 1,
        "content_revision": 1,
        "interaction": "claim",
        "title": "Chapter One",
        "body": "string",
        "primary_action_label": "string",
        "secondary_action_label": "string",
        "learn_more_url": "https://lyricwinter.com/…",
        "available_until": "2026-09-30T17:00:00.000Z",
        "rewards": [
          {
            "reward_key": "string",
            "reward_type": "studio_words",
            "amount_snowcoins": 1,
            "amount_words": 1,
            "expiration_policy": "none",
            "fixed_expires_at": "2026-09-30T17:00:00.000Z",
            "expires_in_seconds": 1
          }
        ]
      }
    ],
    "campaign_offers": [
      {
        "campaign_key": "string",
        "campaign_kind": "promotion",
        "priority": 1,
        "content_revision": 1,
        "interaction": "claim",
        "title": "Chapter One",
        "body": "string",
        "primary_action_label": "string",
        "secondary_action_label": "string",
        "learn_more_url": "https://lyricwinter.com/…",
        "available_until": "2026-09-30T17:00:00.000Z",
        "rewards": [
          {
            "reward_key": "string",
            "reward_type": "studio_words",
            "amount_snowcoins": 1,
            "amount_words": 1,
            "expiration_policy": "none",
            "fixed_expires_at": "2026-09-30T17:00:00.000Z",
            "expires_in_seconds": 1
          }
        ]
      }
    ],
    "purchases": {
      "small_word_packs_eligible": true
    },
    "actions": {
      "prepare_text": {
        "status": "available",
        "billing_mode": "included"
      },
      "speech_generation": {
        "status": "available",
        "billing_mode": "included"
      },
      "sfx_generation": {
        "status": "available",
        "billing_mode": "included"
      },
      "voice_design": {
        "status": "available",
        "billing_mode": "included"
      },
      "voice_cloning": {
        "status": "available",
        "billing_mode": "included"
      },
      "final_render": {
        "status": "available",
        "billing_mode": "included"
      }
    }
  },
  "request_id": "req_01J9Z3K8QF4"
}

List API keys

GET/api-keys

Scope api_keys:read

Lists metadata for the account's API keys, including name, scopes, expiry, last use, and revocation time. Raw keys are never returned.

Response 200

Caller-owned API key metadata without raw key material or token hashes. Fields below are inside data.

  • api_keysarray of objectrequired
    9 item attributes
    • idstringrequireduuid
    • namestringrequired1–80 chars
    • token_prefixstringrequiredpattern ^lw_[A-Za-z0-9_-]{8,32}$

      Safe display prefix for identifying a key.

    • scopesarray of stringrequired

      One ofaccount:readaccount:writeapi_keys:readapi_keys:writeaudio_runs:readaudio_runs:writebilling:readbilling:writedemo_videos:readdemo_videos:writefeedback:writeprojects:readprojects:writereactions:readreactions:writestories:readstories:writestudio:readstudio:writevoice_pins:readvoice_pins:writevoice_actors:readvoice_shares:readvoice_shares:writevoices:readvoices:write

    • expires_atstring | nullrequireddate-time
    • last_used_atstring | nullrequireddate-time
    • revoked_atstring | nullrequireddate-time
    • created_atstringrequireddate-time
    • updated_atstringrequireddate-time

Errors 401, 403, 500 use the standard error envelope.

curl "https://lyricwinter.com/api/v1/api-keys" \
  -H "Authorization: Bearer $LYRICWINTER_API_KEY"
Response 200 (example)
{
  "data": {
    "api_keys": [
      {
        "id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
        "name": "My integration",
        "token_prefix": "string",
        "scopes": [
          "account:read"
        ],
        "expires_at": "2026-09-30T17:00:00.000Z",
        "last_used_at": "2026-09-30T17:00:00.000Z",
        "revoked_at": "2026-09-30T17:00:00.000Z",
        "created_at": "2026-09-30T17:00:00.000Z",
        "updated_at": "2026-09-30T17:00:00.000Z"
      }
    ]
  },
  "request_id": "req_01J9Z3K8QF4"
}

Create an API key

POST/api-keys

Scope api_keys:write

Creates a scoped API key. The response contains raw_key exactly once; LyricWinter stores only a hash. When you call this endpoint with an API key, the new key can only have scopes that the calling key already holds.

Request body application/json

  • namestringrequired1–80 chars
  • scopesarray of stringrequiredmin 1 items

    Duplicate scopes are accepted and deduplicated.

    One ofaccount:readaccount:writeapi_keys:readapi_keys:writeaudio_runs:readaudio_runs:writebilling:readbilling:writedemo_videos:readdemo_videos:writefeedback:writeprojects:readprojects:writereactions:readreactions:writestories:readstories:writestudio:readstudio:writevoice_pins:readvoice_pins:writevoice_actors:readvoice_shares:readvoice_shares:writevoices:readvoices:write

  • expires_atstring | nulldate-time

    Optional expiration timestamp. Null or omitted creates a non-expiring key.

Response 201

API key created. The raw key is returned only in this response. Fields below are inside data.

  • api_keyobjectrequired
    9 child attributes
    • idstringrequireduuid
    • namestringrequired1–80 chars
    • token_prefixstringrequiredpattern ^lw_[A-Za-z0-9_-]{8,32}$

      Safe display prefix for identifying a key.

    • scopesarray of stringrequired

      One ofaccount:readaccount:writeapi_keys:readapi_keys:writeaudio_runs:readaudio_runs:writebilling:readbilling:writedemo_videos:readdemo_videos:writefeedback:writeprojects:readprojects:writereactions:readreactions:writestories:readstories:writestudio:readstudio:writevoice_pins:readvoice_pins:writevoice_actors:readvoice_shares:readvoice_shares:writevoices:readvoices:write

    • expires_atstring | nullrequireddate-time
    • last_used_atstring | nullrequireddate-time
    • revoked_atstring | nullrequireddate-time
    • created_atstringrequireddate-time
    • updated_atstringrequireddate-time
  • raw_keystringrequiredpattern ^lw_[A-Za-z0-9_-]{8,32}_[A-Za-z0-9_-]+$

    Display-once raw API key. The server stores only a SHA-256 hash.

Errors 400, 401, 403, 409, 500 use the standard error envelope.

curl -X POST "https://lyricwinter.com/api/v1/api-keys" \
  -H "Authorization: Bearer $LYRICWINTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "My integration",
  "scopes": [
    "account:read"
  ]
}'
Response 201 (example)
{
  "data": {
    "api_key": {
      "id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
      "name": "My integration",
      "token_prefix": "string",
      "scopes": [
        "account:read"
      ],
      "expires_at": "2026-09-30T17:00:00.000Z",
      "last_used_at": "2026-09-30T17:00:00.000Z",
      "revoked_at": "2026-09-30T17:00:00.000Z",
      "created_at": "2026-09-30T17:00:00.000Z",
      "updated_at": "2026-09-30T17:00:00.000Z"
    },
    "raw_key": "string"
  },
  "request_id": "req_01J9Z3K8QF4"
}

Revoke an API key

DELETE/api-keys/{apiKeyId}

Scope api_keys:write

Revokes one of the account's API keys immediately. Revoking a key that is already revoked succeeds without changes.

Path parameters

  • apiKeyIdstringrequireduuid

Response 200

Caller-owned API key was revoked, or was already revoked. Fields below are inside data.

  • oktruerequired
  • api_keyobjectrequired
    9 child attributes
    • idstringrequireduuid
    • namestringrequired1–80 chars
    • token_prefixstringrequiredpattern ^lw_[A-Za-z0-9_-]{8,32}$

      Safe display prefix for identifying a key.

    • scopesarray of stringrequired

      One ofaccount:readaccount:writeapi_keys:readapi_keys:writeaudio_runs:readaudio_runs:writebilling:readbilling:writedemo_videos:readdemo_videos:writefeedback:writeprojects:readprojects:writereactions:readreactions:writestories:readstories:writestudio:readstudio:writevoice_pins:readvoice_pins:writevoice_actors:readvoice_shares:readvoice_shares:writevoices:readvoices:write

    • expires_atstring | nullrequireddate-time
    • last_used_atstring | nullrequireddate-time
    • revoked_atstring | nullrequireddate-time
    • created_atstringrequireddate-time
    • updated_atstringrequireddate-time

Errors 400, 401, 403, 404, 500 use the standard error envelope.

curl -X DELETE "https://lyricwinter.com/api/v1/api-keys/$API_KEY_ID" \
  -H "Authorization: Bearer $LYRICWINTER_API_KEY"
Response 200 (example)
{
  "data": {
    "ok": true,
    "api_key": {
      "id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
      "name": "My integration",
      "token_prefix": "string",
      "scopes": [
        "account:read"
      ],
      "expires_at": "2026-09-30T17:00:00.000Z",
      "last_used_at": "2026-09-30T17:00:00.000Z",
      "revoked_at": "2026-09-30T17:00:00.000Z",
      "created_at": "2026-09-30T17:00:00.000Z",
      "updated_at": "2026-09-30T17:00:00.000Z"
    }
  },
  "request_id": "req_01J9Z3K8QF4"
}