VisioArtVisioArt Docs

Developer API

Authenticate with API keys, submit generations, track credits, and handle errors safely.

Overview

VisioArt exposes a developer-facing REST API for the same generation pipeline used by the product UI. API requests run against the caller's account, consume the same credit balance, and write the same generation records and usage events that appear in the dashboard.

Use the Developer API when you need to:

  • discover the active model catalog, supported modes, and pricing evidence for your workspace
  • create generations from your own application
  • poll job status without scraping the dashboard
  • download generated assets through a stable API route
  • reconcile credit charges and refunds with your own backend

Base URL

Use your deployed VisioArt origin as the API base:

https://your-domain.com/api/v1

Authentication

Create API keys from Settings -> API Keys. Each key inherits the account's balance and scoped permissions.

API key creation is available to paid plans only.

You can send the key in either header:

Authorization: Bearer visioart_your_api_key

or

x-api-key: visioart_your_api_key

Do not send both headers in the same request unless the values are identical. Mixed credentials are rejected.

Credit Model

  • API generations use the same credit wallet as the signed-in product experience.
  • A generation is charged when the job is queued successfully.
  • Failed or cancelled jobs refund credits automatically when the job transitions to a refundable final state.
  • If the account balance is lower than the estimated cost, generation creation fails with 402 insufficient_credits.

Idempotency

POST /api/v1/generations requires an Idempotency-Key header.

Rules:

  • Replaying the same body with the same key returns the original request result.
  • Reusing the same key with a different request body returns 409 idempotency_conflict.
  • The header accepts up to 200 characters and only allows A-Z a-z 0-9 . _ : -.

Example:

Idempotency-Key: 6d1d0f58-d65b-474a-8d1d-7a91fd8bd8dc

Response Shape

Successful JSON responses include a request identifier:

{
  "request_id": "38285b04-43a5-440f-8bb8-5e8c32e86920",
  "data": {}
}

Error responses use the same envelope:

{
  "request_id": "1f1f8258-ae62-4c65-8e06-f9633dc518df",
  "error": {
    "code": "insufficient_credits",
    "message": "Insufficient credits"
  }
}

The same value is also returned in the x-request-id response header.

Endpoints

List models and capabilities

curl -X GET "https://your-domain.com/api/v1/models" \
  -H "Authorization: Bearer visioart_your_api_key"

Response:

{
  "request_id": "2b4fa4fa-3419-4d92-95ad-d4d08a838bea",
  "data": {
    "object": "list",
    "total": 26,
    "items": [
      {
        "object": "model",
        "model_id": "qwen2",
        "display_name": "Qwen2",
        "availability": "active",
        "modes": [
          {
            "generator_mode": "text-to-image",
            "supported_aspect_ratios": ["16:9", "9:16", "1:1"],
            "supported_duration_seconds": [5],
            "supported_output_formats": ["png", "jpg"],
            "public_model_options": [],
            "pricing": {
              "variants": [
                {
                  "id": "default",
                  "request_model_options": null,
                  "credit_matrix": [
                    {
                      "aspect_ratio": "16:9",
                      "duration_seconds": 5,
                      "credits": 6,
                      "source_type": "legacy_exact_matrix",
                      "source_updated_at": "2026-04-22T11:09:35.262Z"
                    }
                  ]
                }
              ]
            }
          }
        ]
      }
    ]
  }
}

Model directory notes:

  • The endpoint returns the workspace's currently active, enumerable models only. Unsupported or partially configured models are omitted instead of guessed.
  • modes[*].public_model_options lists only option families that are currently valid on the public POST /generations schema and accepted by the business validation path.
  • pricing.variants[*].request_model_options intentionally mirrors the modelOptions request payload shape from POST /generations, so you can copy the object directly.
  • credit_matrix[*].source_type shows where each credit value came from: workspace_override, market_catalog, legacy_exact_matrix, or built_in_estimate.
  • credit_matrix[*].source_updated_at is populated when the value came from a reviewed market catalog rule or the generated exact credit matrix. Override-backed or heuristic rows may return null.

Read credit balance

curl -X GET "https://your-domain.com/api/v1/credits/balance" \
  -H "Authorization: Bearer visioart_your_api_key"

Response:

{
  "request_id": "38285b04-43a5-440f-8bb8-5e8c32e86920",
  "data": {
    "balance": 1000,
    "unit": "credits"
  }
}

Create a generation

curl -X POST "https://your-domain.com/api/v1/generations" \
  -H "Authorization: Bearer visioart_your_api_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6d1d0f58-d65b-474a-8d1d-7a91fd8bd8dc" \
  -d '{
    "title": "veo31-fast text-to-video",
    "modelId": "veo31-fast",
    "generatorMode": "text-to-video",
    "prompt": "A clean product reveal with cinematic camera movement",
    "aspectRatio": "16:9",
    "durationSeconds": 8,
    "uploads": []
  }'

Response:

{
  "request_id": "d587724c-df59-4333-9d4a-2ce8d2988466",
  "idempotent_replay": false,
  "data": {
    "id": "9a87675c-1e1a-4b16-873c-19ffd8e79cdc",
    "object": "generation",
    "status": "processing",
    "title": "veo31-fast text-to-video",
    "model_id": "veo31-fast",
    "generator_mode": "text-to-video",
    "prompt": "A clean product reveal with cinematic camera movement",
    "aspect_ratio": "16:9",
    "duration_seconds": 8,
    "output_format": null,
    "estimated_credits": 30,
    "error_message": null,
    "result_url": null,
    "outputs": [],
    "queued_at": "2026-04-05T13:27:59.824Z",
    "completed_at": null,
    "created_at": "2026-04-05T13:27:59.824Z",
    "updated_at": "2026-04-05T13:28:01.903Z"
  }
}

Request body rules:

  • The public create-generation schema is strict. Unknown or internal-only fields are rejected with 400 invalid_request.
  • uploads only accepts objects shaped like { "kind": "...", "url": "https://..." }.
  • Each upload url must already point to a trusted remote asset URL from workspace storage or a trusted provider-hosted origin.
  • modelOptions, when provided, must only use public option families that apply to the selected modelId. Irrelevant or unknown nested option fields are rejected.
  • imageCount, when present inside modelOptions, is only accepted on models whose /models entry exposes public_model_options[].path = "modelOptions.imageCount"; pricing variants show the exact credit cost before submission.
  • Omit empty no-op objects such as modelOptions: {} or modelOptions: { "kling30": {} }.
  • Do not send internal fields such as landingRecordId, landingKind, landingPathname, estimatedCredits, storagePath, fileName, or required.

Generation response fields:

FieldTypeNotes
idstring (UUID)VisioArt generation job id.
object"generation"Constant literal.
status"queued" | "processing" | "completed" | "failed" | "cancelled"Terminal states: completed, failed, cancelled.
titlestringDefaults to "{modelId} {generatorMode}" when omitted in the request.
model_idstringThe model the request ran against.
generator_modestringtext-to-video, image-to-video, video-to-video, effect, image-editor, text-to-image, or video-compression.
promptstringMay be empty for modes that do not require a prompt.
aspect_ratiostringOne of 16:9, 9:16, 1:1, 4:3, 3:4, 3:2, 2:3.
duration_secondsnumberInteger in 3-15.
output_format"png" | "jpg" | "webp" | nullOnly populated for image modes that accept an output format.
estimated_creditsnumberCredits charged at queue time; refunded on failed or safely cancelled.
error_messagestring | nullPopulated only on failed.
result_urlstring | nullShortcut for outputs[0].url. Null while outputs is empty.
outputsGenerationOutput[]See table below. Empty until the job produces assets.
queued_atstring (ISO-8601)When the job entered the queue.
completed_atstring | null (ISO-8601)Set when the job reaches a terminal state.
created_atstring (ISO-8601)Row creation timestamp.
updated_atstring (ISO-8601)Last state-transition timestamp.

Generation output object (outputs[i]):

FieldTypeNotes
idstring (UUID)Asset id; usable against /assets/{assetId}.
kindstringTypically video or image; mirrors the generator mode.
urlstringVisioArt asset route (/api/v1/assets/{id}), not the raw provider URL.
mime_typestring | nullPopulated once the provider reports the asset mime type.
widthnumber | nullPixels; null until the asset metadata is known.
heightnumber | nullPixels; null until the asset metadata is known.
duration_secondsnumber | nullNull for image outputs.
created_atstring (ISO-8601)Asset row creation timestamp.

Read a generation

curl -X GET "https://your-domain.com/api/v1/generations/9a87675c-1e1a-4b16-873c-19ffd8e79cdc" \
  -H "Authorization: Bearer visioart_your_api_key"

Cancel a generation

curl -X POST "https://your-domain.com/api/v1/generations/9a87675c-1e1a-4b16-873c-19ffd8e79cdc/cancel" \
  -H "Authorization: Bearer visioart_your_api_key"

Cancellation is only available while the job is still in VisioArt's local queue and has not started upstream submission yet.

In practice, that means the job must still be queued, must not have a provider task ID yet, and must not already be claimed by a worker for submission.

If cancellation succeeds, the job transitions to cancelled and writes a refund event for the previously charged credits.

Once a job has started the upstream submission flow, the API returns 409 generation_not_cancellable. This avoids promising a cancellation that the upstream provider may not actually support.

Read usage events

curl -X GET "https://your-domain.com/api/v1/usage?limit=20&offset=0" \
  -H "Authorization: Bearer visioart_your_api_key"

Query parameters:

NameTypeDefaultRangeNotes
limitnumber201-100Page size.
offsetnumber00-10000Zero-based offset.
apiKeyIdstring1-255 charsOptional filter restricting the response to events produced by a specific API key.

Response envelope:

{
  "request_id": "…",
  "data": {
    "items": [
      {
        "id": "…",
        "api_request_id": "…",
        "api_key_id": "…",
        "generation_job_id": "…",
        "credit_transaction_id": "…",
        "event_type": "charge",
        "credits_delta": -20,
        "balance_before": 1000,
        "balance_after": 980,
        "pricing_snapshot": "{\"schemaVersion\":1,\"modelId\":\"veo31-fast\",\"generatorMode\":\"text-to-video\",\"aspectRatio\":\"16:9\",\"durationSeconds\":8,\"modelOptions\":null,\"variantKey\":null,\"credits\":20,\"listCredits\":20,\"hasPromotion\":false,\"promotionLabel\":null,\"promotionStartsAt\":null,\"promotionEndsAt\":null,\"sourceType\":\"market_catalog\",\"sourceUpdatedAt\":\"2026-05-15\"}",
        "note": null,
        "created_at": "2026-04-05T13:27:59.824Z"
      }
    ],
    "pagination": { "limit": 20, "offset": 0, "total": 137 }
  }
}

Usage item fields:

FieldTypeNotes
idstring (UUID)Usage event id.
api_request_idstring (UUID)Links the event to the originating API request record.
api_key_idstringThe key that produced the event.
generation_job_idstring | nullThe job that produced the charge or refund.
credit_transaction_idstring | nullCredit ledger row id.
event_type"charge" | "refund"charge is written when a job is queued; refund is written when a previously charged job transitions to failed or a safely cancellable cancelled state.
credits_deltanumberNegative for charges, positive for refunds.
balance_beforenumber | nullWallet balance before the event, in credits.
balance_afternumber | nullWallet balance after the event, in credits.
pricing_snapshotstring | nullJSON-stringified pricing metadata captured at charge time. Parse it if you need structured fields.
notestring | nullFree-form operator note, if any.
created_atstring (ISO-8601)Event timestamp.

The pagination.total field reflects the total number of events matching the query, not the current page.

Download an asset

curl -L "https://your-domain.com/api/v1/assets/510522c3-f7fb-4265-82ae-471f12aea62b" \
  -H "Authorization: Bearer visioart_your_api_key" \
  -o output.mp4

Common Error Codes

VisioArt validates generation requests in two layers:

  • Schema layer (400 invalid_request) — Shape, type, and format checks. Unknown fields, wrong types, values outside the declared enums or lengths, or internal-only keys (for example landingRecordId, estimatedCredits) are rejected here before any business logic runs.
  • Business layer (422 invalid_generation_request) — Semantic checks that depend on workspace state or the resolved model capability. A well-formed request can still fail here if the modelId is not active in the workspace, the combination of modelId × generatorMode × aspectRatio × durationSeconds is not supported, a required upload is missing, an upload URL is not trusted, or the resolved pricing rule is missing.

The set of active models and their supported options is workspace-scoped and can be overridden by administrators, so there is no hard-coded modelId enum on the public schema. Read GET /api/v1/models before large batch submissions or treat 422 invalid_generation_request as a retryable intent to fix input.

HTTPCodeMeaning
400ambiguous_api_keyMore than one different API key credential was sent.
400invalid_content_lengthRequest metadata included an invalid Content-Length value.
400invalid_requestJSON schema validation failed (shape, type, format).
400invalid_jsonRequest body is not valid JSON.
400missing_idempotency_keyCreate-generation request omitted Idempotency-Key.
400invalid_idempotency_keyIdempotency-Key format is invalid.
404feature_disabledDeveloper API is disabled in this workspace.
401missing_api_keyNo API key was provided.
401invalid_api_keyAPI key is malformed, unknown, or not recoverable.
401api_key_disabledAPI key exists but is disabled.
401api_key_expiredAPI key exists but is expired.
401invalid_authorization_headerAuthorization header is not using Bearer.
403insufficient_scopeThe API key does not include the required permission scope.
402insufficient_creditsThe account balance is lower than the generation estimate.
409idempotency_conflictThe same idempotency key was reused with a different body.
409generation_not_cancellableThe job is outside VisioArt's local cancellation window and can no longer be cancelled safely.
413body_too_largeRequest body exceeds the current limit.
415unsupported_media_typeEndpoint only accepts JSON payloads.
422invalid_generation_requestRequest shape is valid but the resolved generation intent is not supported (unsupported model, incompatible option combination, untrusted upload URL, missing pricing rule).
429concurrent_job_limitThe account already has the maximum number of active API generation jobs.
429rate_limitedAPI key request rate limit has been exceeded.
500internal_errorAn unexpected developer API error occurred before route-specific handling.
500models_read_failedThe model directory could not be loaded.
500request_state_invalidStored idempotency state is incomplete and cannot be replayed safely.
503provider_unavailableProvider balance or provider availability blocked submission.

Endpoint-Specific Error Notes

Model directory

  • 500 models_read_failed

Generation creation

  • 400 invalid_request
  • 402 insufficient_credits
  • 409 idempotency_conflict
  • 429 concurrent_job_limit
  • 422 invalid_generation_request
  • 500 request_state_invalid
  • 503 provider_unavailable
  • 500 generation_create_failed

Generation read

  • 400 invalid_generation_id
  • 404 generation_not_found
  • 500 generation_read_failed

Generation cancel

  • 400 invalid_generation_id
  • 404 generation_not_found
  • 409 generation_not_cancellable
  • 500 generation_cancel_failed

Billing Edge Cases

  • 402 insufficient_credits is returned before a new generation job is inserted. No generation ID is created, no credit charge is written, and no refund event is needed.
  • Refunds are only written when a previously charged job transitions to failed or to a safely cancellable cancelled state.
  • Job status transition, refund ledger mutation, developer API refunded counters, and refund usage events are persisted in the same database transaction. If any local refund persistence step fails, VisioArt rolls the transition back instead of leaving a partial refund state behind.

Credit balance

  • 500 credits_read_failed

Usage list

  • 400 invalid_query
  • 500 usage_read_failed

Asset read

  • 400 invalid_asset_id
  • 404 asset_not_found
  • 500 asset_read_failed

Permission Scopes

New API keys receive these default scopes:

{
  "generation": ["create", "read", "cancel"],
  "credits": ["read"],
  "usage": ["read"],
  "assets": ["read"],
  "models": ["read"]
}

If a key was created before scoped permissions existed, VisioArt preserves backward compatibility and still allows requests unless the key is explicitly restricted later. If a key carries an explicit permissions payload, VisioArt enforces that payload as-is; backward compatibility only applies to legacy keys that predate scoped permissions and therefore have no stored permissions field.

Operational Notes

  • The asset URL inside a generation response points to the VisioArt API asset route, not to the raw provider URL.
  • Refunds are written to both the user credit ledger and the developer API usage log.
  • Credit charges and refunds are part of the same database lifecycle as the generation state transition, which prevents partial refund states where balance changes but job status does not.

Recommended integration flow

  1. Read /models to discover the workspace's current model catalog, valid option families, and credit evidence.
  2. Read /credits/balance before large batch submissions.
  3. Create jobs with a unique Idempotency-Key.
  4. Poll /generations/{jobId} until the job reaches a final state.
  5. Use /usage to reconcile charges and refunds in your own system.

Table of Contents