API reference
Account and API keys API
Check API health, read the account 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
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.
oktruerequiredservice"lyricwinter"requiredversion"v1"required
curl "https://lyricwinter.com/api/v1/health" \
-H "Authorization: Bearer $LYRICWINTER_API_KEY"const response = await fetch(`https://lyricwinter.com/api/v1/health`, {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.LYRICWINTER_API_KEY}`,
},
});
const { data, error, request_id } = await response.json();import os
import requests
response = requests.request(
"GET",
f"https://lyricwinter.com/api/v1/health",
headers={
"Authorization": f"Bearer {os.environ['LYRICWINTER_API_KEY']}",
},
)
payload = response.json(){
"data": {
"ok": true,
"service": "lyricwinter",
"version": "v1"
},
"request_id": "req_01J9Z3K8QF4"
}Get the current account
GET/me
account:readReturns 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.
profileobjectrequired5 child attributes
idstringrequiredusernamestring | nullrequired3–30 charsnamestring | nullrequiredmax 100 charsimagestring | nullrequireduriemailstring | nullrequiredemail
Errors 401, 403, 500 use the standard error envelope.
curl "https://lyricwinter.com/api/v1/me" \
-H "Authorization: Bearer $LYRICWINTER_API_KEY"const response = await fetch(`https://lyricwinter.com/api/v1/me`, {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.LYRICWINTER_API_KEY}`,
},
});
const { data, error, request_id } = await response.json();import os
import requests
response = requests.request(
"GET",
f"https://lyricwinter.com/api/v1/me",
headers={
"Authorization": f"Bearer {os.environ['LYRICWINTER_API_KEY']}",
},
)
payload = response.json(){
"data": {
"profile": {
"id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10",
"username": "string",
"name": "My integration",
"image": "https://lyricwinter.com/…",
"email": "[email protected]"
}
},
"request_id": "req_01J9Z3K8QF4"
}List API keys
GET/api-keys
api_keys:readLists 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 objectrequired9 item attributes
idstringrequireduuidnamestringrequired1–80 charstoken_prefixstringrequiredpattern ^lw_[A-Za-z0-9_-]{8,32}$Safe display prefix for identifying a key.
scopesarray of stringrequiredOne of
account: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:writeexpires_atstring | nullrequireddate-timelast_used_atstring | nullrequireddate-timerevoked_atstring | nullrequireddate-timecreated_atstringrequireddate-timeupdated_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"const response = await fetch(`https://lyricwinter.com/api/v1/api-keys`, {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.LYRICWINTER_API_KEY}`,
},
});
const { data, error, request_id } = await response.json();import os
import requests
response = requests.request(
"GET",
f"https://lyricwinter.com/api/v1/api-keys",
headers={
"Authorization": f"Bearer {os.environ['LYRICWINTER_API_KEY']}",
},
)
payload = response.json(){
"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
api_keys:writeCreates 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 charsscopesarray of stringrequiredmin 1 itemsDuplicate scopes are accepted and deduplicated.
One of
account: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:writeexpires_atstring | nulldate-timeOptional 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_keyobjectrequired9 child attributes
idstringrequireduuidnamestringrequired1–80 charstoken_prefixstringrequiredpattern ^lw_[A-Za-z0-9_-]{8,32}$Safe display prefix for identifying a key.
scopesarray of stringrequiredOne of
account: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:writeexpires_atstring | nullrequireddate-timelast_used_atstring | nullrequireddate-timerevoked_atstring | nullrequireddate-timecreated_atstringrequireddate-timeupdated_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"
]
}'const response = await fetch(`https://lyricwinter.com/api/v1/api-keys`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LYRICWINTER_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"name": "My integration",
"scopes": [
"account:read"
]
}),
});
const { data, error, request_id } = await response.json();import os
import requests
response = requests.request(
"POST",
f"https://lyricwinter.com/api/v1/api-keys",
headers={
"Authorization": f"Bearer {os.environ['LYRICWINTER_API_KEY']}",
},
json={
"name": "My integration",
"scopes": [
"account:read"
]
},
)
payload = response.json(){
"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}
api_keys:writeRevokes 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.
oktruerequiredapi_keyobjectrequired9 child attributes
idstringrequireduuidnamestringrequired1–80 charstoken_prefixstringrequiredpattern ^lw_[A-Za-z0-9_-]{8,32}$Safe display prefix for identifying a key.
scopesarray of stringrequiredOne of
account: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:writeexpires_atstring | nullrequireddate-timelast_used_atstring | nullrequireddate-timerevoked_atstring | nullrequireddate-timecreated_atstringrequireddate-timeupdated_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"const apiKeyId = "<apiKeyId>";
const response = await fetch(`https://lyricwinter.com/api/v1/api-keys/${apiKeyId}`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.LYRICWINTER_API_KEY}`,
},
});
const { data, error, request_id } = await response.json();import os
import requests
api_key_id = "<apiKeyId>"
response = requests.request(
"DELETE",
f"https://lyricwinter.com/api/v1/api-keys/{api_key_id}",
headers={
"Authorization": f"Bearer {os.environ['LYRICWINTER_API_KEY']}",
},
)
payload = response.json(){
"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"
}