Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: BeatAPI Developer Decisions API
version: 1.0.0-launch
license:
name: BeatAPI Terms of Service
url: https://beatapi.io/terms-of-service
description: 'BeatAPI provides one API key for public text models, image generation, video generation,
video analysis, Effects, asynchronous video workflows, and short-lived Realtime Video Sessions.
For asynchronous operations, create a task, poll the shared task endpoint or receive webhook events,
and read hosted output URLs from `output.media`. For Realtime, create a Session on a trusted server
with your Bearer API key and pass only the returned short-lived `client_secret` to the browser.
Customer balances and usage are USD-denominated. Compatibility fields such
as `credit_balance` and `credits_reserved` remain in the API; 1 Credit = $1 USD.
## 5 minute Quick Start
1. Set your base URL to `https://api.beatapi.io`.
2. Create an API key in Dashboard → API Keys
and send it as
`Authorization: Bearer `.
USD balance top-ups are available from
Dashboard → Billing.
3. Use public HTTPS URLs for input media. If your files are local, upload
them with `POST /v1/files` first.
4. Create a workflow task.
5. Poll every 5-10 seconds until `status` is `succeeded` or `failed`.
6. Add webhooks later if you do not want to poll.
```bash
read -rsp "BeatAPI API key: " BEATAPI_API_KEY && echo
export BEATAPI_API_KEY
curl https://api.beatapi.io/v1/workflows
curl https://api.beatapi.io/v1/music-video/tasks \
-H "Authorization: Bearer $BEATAPI_API_KEY" \
-H "Content-Type: application/json" \
-d ''{
"images": ["https://cdn.beatapi.io/samples/neon-singer.png"],
"audio_url": "https://cdn.beatapi.io/samples/neon-singer-preview.mp3",
"prompt": "Neon rooftop performance with cinematic light trails.",
"language": "en",
"resolution": "720p"
}''
curl https://api.beatapi.io/v1/tasks/task_8K2qA \
-H "Authorization: Bearer $BEATAPI_API_KEY"
```
## How tasks work
A task starts as `queued` or `processing`. Keep polling
`GET /v1/tasks/{task_id}` every 5-10 seconds with a little jitter. Stop when
the task is `succeeded` or `failed`.
- `queued`: accepted and waiting for platform capacity.
- `processing`: generation is running.
- `storyboard_ready`: Music Video storyboard shots are available.
- `requires_action`: choose storyboard shots and call compose.
- `editing`: a storyboard shot edit is running.
- `composing`: selected storyboard shots are being composed.
- `succeeded`: output is ready in `output.media[]`.
- `failed`: no usable output was produced. Inspect `error_code`,
`error_message`, and `usage` for the final task result.
The current 10 minute processing window is an operational timeout for launch
testing, not a completion SLA.
## Inputs and limits
Public workflow inputs must use HTTPS URLs that are reachable from the
public internet. Localhost, private network URLs, and data URLs are rejected.
Use `POST /v1/files` for local images, audio, subtitles, or Motion Control reference videos.
Each verified new user account starts with a $2 welcome balance that never expires and
1 active processing concurrency. Lifetime paid purchases unlock higher limits:
$10+ = 2, $100+ = 5, $1,000+ = 10, $5,000+ = 15, and $20,000+ = 30.
`GET /v1/usage` returns current usage totals, concurrency limit, and active
processing task count. Active concurrency measures tasks that are currently
using BeatAPI processing resources. storyboard_ready and requires_action
tasks can have settled USD usage but do not count toward active processing
concurrency.
## Webhooks are optional
Polling is the simplest integration path. If you add webhooks, BeatAPI sends
`task.succeeded` and `task.failed` events. Webhook delivery failures do not
change task status; polling remains the source of truth.'
servers:
- url: https://api.beatapi.io
tags:
- name: Decisions
paths:
/v1/systemone:
post:
operationId: createDecision
tags:
- Decisions
summary: Answer typed decision questions with JEV
description: Synchronous native decision endpoint. Send shared state, optionally with images in assets, and named noul, choice, score or box questions; read id, model, answers, and usage directly from the response (no data envelope and no task polling). Nouls require instructions; score accepts 1-10 criteria; box (jev-1.14) needs an image. The paid model is billed by input tokens, images included; the free model is rate limited. Inspect the selected model for current availability and pricing. This is the direct equivalent of capabilities/run with reference model:jev-1.14 or model:jev-1.14-free.
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DecisionRequest'
example:
model: jev-1.14-free
state: A user requested a reversible draft update.
questions:
safe_to_run:
type: noul
instructions: Is this safe to run without additional approval?
responses:
'200':
description: Completed decision; no polling is required.
content:
application/json:
schema:
$ref: '#/components/schemas/DecisionResponse'
example:
id: task_example_decision
model: jev-1.14-free
answers:
safe_to_run:
type: noul
noul: 0.95
usage:
input_tokens: 42
output_tokens: 0
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/InsufficientCredits'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ProcessingUnavailable'
components:
responses:
Unauthorized:
description: 'No API key (`missing_api_key`), or a key that is wrong, expired, disabled or exhausted (`invalid_api_key`; the message says which). Send it as `Authorization: Bearer <key>`; the key works with or without the `sk-` prefix. Not retryable.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: invalid_api_key
message: 'Invalid or inactive API key. Send it as Authorization: Bearer <key>; the key works with or without the sk- prefix.'
retryable: false
request_id: req_xxx
Forbidden:
description: The key or account is not permitted to make this call (`forbidden`), for example a model that is not available on free credit (a top-up unlocks it), a request from outside the key's IP allowlist, or a model outside the key's group. Not retryable.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: forbidden
message: This model is not available on free credit. Top up to use it.
retryable: false
request_id: req_xxx
BadRequest:
description: Malformed JSON or a field, value or combination this endpoint does not accept (`bad_request`; the message names the field), or input refused by moderation (`content_policy_violation`). Not retryable unchanged.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: bad_request
message: images must contain 1-7 public HTTPS URLs.
retryable: false
request_id: req_xxx
NotFound:
description: Unknown model or capability, or a task that does not exist for this account (`not_found`). Not retryable.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: not_found
message: The requested resource was not found.
retryable: false
request_id: req_xxx
InsufficientCredits:
description: Insufficient balance (`insufficient_credits`). The account balance is zero or below the price of this request. Top up, then send it again; not retryable before that.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: insufficient_credits
message: Account balance is not sufficient for this task.
retryable: false
request_id: req_xxx
InternalError:
description: Unexpected internal error (`internal_error`). Retryable; keep `request_id` for support if it persists.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: internal_error
message: Internal error. Contact support with the request_id if the problem continues.
retryable: true
request_id: req_xxx
ProcessingUnavailable:
description: The request could not be completed (`processing_unavailable`, `processing_timeout`, `processing_failed` or `result_transfer_failed`). Nothing was charged. Retryable with backoff; a 503 carries no `Retry-After` header and no `retry_after_seconds`. BeatAPI does not answer 502 or 504.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: processing_unavailable
message: This model is temporarily unavailable on our side. Retry in a few minutes or use another model; this request was not charged.
retryable: true
request_id: req_xxx
RateLimited:
description: Too many requests (`rate_limit_exceeded`, including the free-model limits). Retryable after `Retry-After` seconds (also `error.retry_after_seconds`).
headers:
Retry-After:
description: Seconds to wait before retrying the request.
schema:
type: integer
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: rate_limit_exceeded
message: Too many polling requests. Poll every 5-10 seconds.
retryable: true
request_id: req_xxx
retry_after_seconds: 12
schemas:
DecisionAsset:
type: object
required:
- format
- data_base64
properties:
type:
type: string
enum:
- image
format:
type: string
enum:
- png
- jpg
- jpeg
data_base64:
type: string
minLength: 1
description: Standard base64 of the image bytes, without a data URL prefix.
DecisionResponse:
type: object
required:
- id
- model
- answers
- usage
properties:
id:
type: string
description: Identifier for the completed decision.
model:
type: string
answers:
type: object
description: One typed answer per question name, without a data envelope.
additionalProperties:
type: object
required:
- type
properties:
type:
type: string
enum:
- noul
- choice
- score
- box
noul:
type: number
minimum: 0
maximum: 1
description: Likelihood of yes.
box:
type: object
description: Edges as fractions of the image's height and width, 0-1, measured from the top and the left.
properties:
top:
type: number
bottom:
type: number
left:
type: number
right:
type: number
choice:
type: string
description: Chosen option key.
score:
type: number
description: Continuous zero-based position on the scale.
probabilities:
type: object
additionalProperties:
type: number
confidence:
type:
- number
- 'null'
legend:
type: object
additionalProperties: {}
usage:
type: object
required:
- input_tokens
- output_tokens
properties:
input_tokens:
type: integer
minimum: 0
output_tokens:
type: integer
minimum: 0
DecisionRequest:
type: object
required:
- state
- questions
properties:
model:
type: string
default: jev-1.14
description: Use a published decision model, such as jev-1.14 or jev-1.14-free; jev-1.13 and jev-1.13-free remain available.
state:
description: Shared application state. Billed once across all named questions.
oneOf:
- type: string
minLength: 1
pattern: \S
- type: object
additionalProperties: true
- type: array
items: {}
questions:
type: object
minProperties: 1
additionalProperties:
oneOf:
- $ref: '#/components/schemas/DecisionNoulQuestion'
- $ref: '#/components/schemas/DecisionChoiceQuestion'
- $ref: '#/components/schemas/DecisionScoreQuestion'
- $ref: '#/components/schemas/DecisionBoxQuestion'
assets:
type: object
minProperties: 1
description: Images, each under a reference string that you also write in state, instructions or criteria where the picture belongs. PNG or JPEG, 280 to 1568 px a side, up to 16 MiB each; the whole body up to 32 MiB. Images are billed as input tokens. JSON only; multipart is not accepted.
additionalProperties:
$ref: '#/components/schemas/DecisionAsset'
stream:
type: boolean
enum:
- false
description: Only false is supported; decisions are synchronous and never stream.
DecisionScoreQuestion:
type: object
required:
- type
- criteria
properties:
type:
type: string
enum:
- score
instructions:
oneOf:
- $ref: '#/components/schemas/DecisionInstructions'
- type: 'null'
criteria:
type: array
minItems: 1
maxItems: 10
items:
$ref: '#/components/schemas/DecisionEntry'
description: Ordered scale labels, lowest first. More than 10 is rejected.
Error:
type: object
required:
- error
properties:
error:
type: object
description: Structured BeatAPI error. Use `code` (or `retryable`) for program logic and retain `request_id` for support.
required:
- code
- message
- request_id
properties:
code:
type: string
description: 'Stable machine-readable error code. `unauthorized` is the legacy name for a 401; since 2026-09-29 a 401 is `missing_api_key` or `invalid_api_key`, but `unauthorized` may still appear. Treat it like `invalid_api_key`. `result_expired` is returned only by `POST /v1/capabilities/run` with `operation: "result"`.'
enum:
- bad_request
- missing_api_key
- invalid_api_key
- unauthorized
- forbidden
- not_found
- insufficient_credits
- idempotency_conflict
- rate_limit_exceeded
- content_policy_violation
- processing_unavailable
- processing_failed
- processing_timeout
- result_transfer_failed
- result_expired
- not_implemented
- realtime_disabled
- realtime_capacity_unavailable
- realtime_session_expired
- origin_not_allowed
- invalid_client_secret
- transport_not_allowed
- internal_error
message:
type: string
description: English sentence that states the cause; for `bad_request` it names the field. Written for people and logs, not for parsing.
retryable:
type: boolean
description: Whether making the same call again could succeed. Present for every code in the public table; it may be absent only for a code outside the table. True for rate_limit_exceeded, internal_error, processing_unavailable, processing_timeout, processing_failed, result_transfer_failed and realtime_capacity_unavailable. False for a request, key or balance that must change first (bad_request, content_policy_violation, missing_api_key, invalid_api_key, unauthorized, insufficient_credits, forbidden, not_found, idempotency_conflict, result_expired, not_implemented, realtime_disabled and the other realtime credential codes).
request_id:
type: string
description: Correlation ID to retain for BeatAPI support.
retry_after_seconds:
type: integer
description: Seconds to wait before retrying. Sent only with a 429 `rate_limit_exceeded`, and the same value is sent in the `Retry-After` header. No other code carries it.
docs:
type: string
format: uri
description: The public Skill page that says what to do about an error. Present on errors answered by the capability endpoints (`/v1/capabilities/search`, `/v1/capabilities/inspect`, `/v1/capabilities/run`); absent on every other endpoint.
DecisionChoiceQuestion:
type: object
required:
- type
- criteria
properties:
type:
type: string
enum:
- choice
instructions:
oneOf:
- $ref: '#/components/schemas/DecisionInstructions'
- type: 'null'
criteria:
type: object
minProperties: 1
maxProperties: 255
additionalProperties:
$ref: '#/components/schemas/DecisionEntry'
description: Option key to its meaning; one call can rank many options.
DecisionBoxQuestion:
type: object
required:
- type
description: Locates what the instructions describe in the request's image. jev-1.14 only; the request needs an image in assets.
properties:
type:
type: string
enum:
- box
instructions:
$ref: '#/components/schemas/DecisionInstructions'
DecisionNoulQuestion:
type: object
required:
- type
- instructions
properties:
type:
type: string
enum:
- noul
instructions:
$ref: '#/components/schemas/DecisionInstructions'
criteria:
description: Optional descriptions of true and false.
oneOf:
- type: object
additionalProperties: false
properties:
'true':
$ref: '#/components/schemas/DecisionEntry'
'false':
$ref: '#/components/schemas/DecisionEntry'
- type: 'null'
DecisionEntry:
description: A criterion description; text, JSON object, array, or null.
oneOf:
- type: string
- type: object
additionalProperties: true
- type: array
items: {}
- type: 'null'
DecisionInstructions:
description: The question in words, or structured instructions. Required for noul.
oneOf:
- type: string
minLength: 1
pattern: \S
- type: object
additionalProperties: true
- type: array
minItems: 1
items: {}
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: sk_xxx
description: 'Send your API key as `Authorization: Bearer <BEATAPI_API_KEY>`.'
PersonalAccessToken:
type: http
scheme: bearer
bearerFormat: nap_xxx
description: 'Send the account''s personal access token as `Authorization: Bearer <token>`. Create it on the Dashboard''s Account security page. API keys are not accepted.'
ApiKeyHeader:
type: apiKey
in: header
name: x-api-key
description: Anthropic-compatible API key header. Use a BeatAPI API key.
GoogleApiKeyHeader:
type: apiKey
in: header
name: x-goog-api-key
description: Gemini-compatible API key header. Use a BeatAPI API key.
GoogleApiKeyQuery:
type: apiKey
in: query
name: key
description: Gemini SDK compatibility only. Prefer the x-goog-api-key header when possible.
externalDocs:
description: BeatAPI documentation
url: https://docs.beatapi.io