> ## Documentation Index
> Fetch the complete documentation index at: https://docs.framelane.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Verify email with OTP code

> Submit the 6-digit OTP sent to the email supplied during signup. On success the workspace is marked as email-verified and the OTP is consumed. After 5 wrong guesses the code is invalidated and a new signup call is required to obtain a fresh one.



## OpenAPI

````yaml /openapi.json post /v1/signup/verify
openapi: 3.1.0
info:
  title: Framelane API
  description: >
    Framelane renders video and runs AI media tasks from a single declarative
    request.


    ### Getting access (self-serve, no human needed)

    You can provision your own workspace and API key end-to-end — no dashboard,
    no sales call:

    1. `POST /v1/signup` with a `workspace_name` and an `email` you control. You
    get an API key
       back immediately and a 6-digit code is emailed to that address.
    2. Read the code from that inbox and `POST /v1/signup/verify` with it. If
    you drive a
       programmatic inbox (e.g. an agent mail service), this whole loop runs unattended.
    3. Your key works the moment the email is verified. Until then every authed
    call returns
       `403 email_not_verified` — verification is the only gate, so an unverified workspace can
       never spend. Re-calling `POST /v1/signup` re-keys the workspace and requires re-verifying.

    `POST /v1/signup`, `POST /v1/signup/verify`, and `GET /v1/capabilities` need
    **no** API key.


    ### Authentication

    Send your API key as a bearer token: `Authorization: Bearer fl_live_...`.


    ### Core workflow

    1. **Get media in.** Pass any publicly accessible URL directly, or `POST
    /v1/uploads` to
       get a signed URL, then `PUT` your file to it.
    2. **Submit one JSON payload.** `POST /v1/renders` composes a whole scene
    (canvas +
       `elements[]` + `transitions[]`) in a single request; `POST /v1/tasks/{type}` runs an AI
       operation (transcribe).
    3. **Wait for completion.** Jobs are async: poll `GET
    /v1/{renders|tasks}/{id}` or register
       a webhook. Statuses end in `completed`, `failed`, or `cancelled`.
    4. **Fetch the result.** A render's artifact is a file: use `output.url` or
       `GET /v1/renders/{id}/download` for a short-lived signed URL. A task that returns data
       puts it on the job's `result` object — `transcribe` returns its transcript there, has no
       `output`, and `404`s on `/v1/tasks/{id}/download`.

    ### Discovering what's possible

    Call **`GET /v1/capabilities`** (no auth) for the machine-readable catalog
    of every effect,

    motion, transition, format, element type, and task parameter — each flagged
    with whether the

    renderer supports it, plus all numeric ranges and rate limits. Validate
    against it before

    submitting to avoid `422`s.


    ### Idempotency

    `POST` endpoints accept an `Idempotency-Key` header. The same key with the
    same body replays

    the original response (`200`); with a different body it returns `409
    Conflict`.


    ### Errors

    Every error has the shape `{"error": {"code", "message", "details"}}`. The
    machine-readable

    `code` (e.g. `source_not_found`, `quota_exceeded`, `codec_unsupported`) is
    stable — branch on

    it to self-correct.
  version: 0.2.0
servers:
  - url: https://api.framelane.io
    description: Production
security: []
tags:
  - name: capabilities
    description: Discover supported features, formats, and limits.
  - name: renders
    description: Compose and render video from a declarative timeline.
  - name: projects
    description: >-
      Stateful editing: read a composition, apply targeted ops, preview, and
      render the head.
  - name: preview
    description: >-
      Validate a composition for free and preview a frame or window cheaply,
      without a full render.
  - name: tasks
    description: 'Run AI media operations: transcription.'
  - name: uploads
    description: Get signed URLs to upload source media into Framelane storage.
  - name: webhooks
    description: Subscribe to job lifecycle events with signed delivery.
  - name: workspace
    description: Manage your workspace, usage, and assets.
  - name: api-keys
    description: Create and revoke API keys.
  - name: auth
    description: Session sync for the first-party console.
  - name: signup
    description: Create a workspace and verify email.
  - name: billing
    description: Manage your subscription and billing portal.
  - name: system
    description: Health, readiness, and version probes.
paths:
  /v1/signup/verify:
    post:
      tags:
        - signup
      summary: Verify email with OTP code
      description: >-
        Submit the 6-digit OTP sent to the email supplied during signup. On
        success the workspace is marked as email-verified and the OTP is
        consumed. After 5 wrong guesses the code is invalidated and a new signup
        call is required to obtain a fresh one.
      operationId: verify_signup
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SignupVerifyRequest'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkspaceOut'
        '400':
          description: Invalid or expired OTP code.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '404':
          description: No workspace found for this email.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '429':
          description: Rate limit exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
components:
  schemas:
    SignupVerifyRequest:
      properties:
        email:
          type: string
          maxLength: 320
          format: email
          title: Email
          description: The email address used during signup.
          example: dev@acmecorp.com
        otp_code:
          type: string
          maxLength: 6
          minLength: 6
          pattern: ^\d{6}$
          title: Otp Code
          description: The 6-digit OTP sent to the email.
          example: '123456'
      additionalProperties: false
      type: object
      required:
        - email
        - otp_code
      title: SignupVerifyRequest
    WorkspaceOut:
      properties:
        id:
          type: string
          title: Id
          description: Unique workspace ID.
          example: ws_01J8QR2K5VKDGN2T4FBM3CZYX8
        name:
          type: string
          title: Name
          description: Display name for the workspace.
          example: Acme Corp
        email:
          anyOf:
            - type: string
            - type: 'null'
          title: Email
          description: Contact email for the workspace owner.
          example: dev@acmecorp.com
        email_verified:
          type: boolean
          title: Email Verified
          description: Whether the email address has been verified via OTP.
          default: false
        plan:
          $ref: '#/components/schemas/WorkspacePlan'
          description: 'Current billing plan: `free`, `pro`, or `enterprise`.'
          default: free
          example: free
        plan_status:
          $ref: '#/components/schemas/WorkspacePlanStatus'
          description: 'Billing status: `active`, `past_due`, `cancelled`, or `trialing`.'
          default: active
          example: active
        created_at:
          type: string
          format: date-time
          title: Created At
          description: ISO-8601 UTC timestamp when the workspace was created.
      type: object
      required:
        - id
        - name
        - created_at
      title: WorkspaceOut
    ApiError:
      description: >-
        The error envelope returned on every 4xx/5xx response: `{"error":
        {...}}`.
      properties:
        error:
          $ref: '#/components/schemas/ErrorResponse'
      required:
        - error
      title: ApiError
      type: object
    WorkspacePlan:
      type: string
      enum:
        - free
        - pro
        - enterprise
      title: WorkspacePlan
    WorkspacePlanStatus:
      type: string
      enum:
        - active
        - past_due
        - cancelled
        - trialing
      title: WorkspacePlanStatus
    ErrorResponse:
      description: >-
        The structured error body — the value of the `error` key on an error
        response.
      properties:
        code:
          $ref: '#/components/schemas/ApiErrorCode'
        message:
          title: Message
          type: string
        details:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
          default: null
          title: Details
      required:
        - code
        - message
      title: ErrorResponse
      type: object
    ApiErrorCode:
      description: >-
        Machine-readable error code returned on a 4xx/5xx response. Branch on
        `error.code` to

        handle failures programmatically.


        Values:

        - `invalid_request` — The request was malformed or failed validation.

        - `unauthorized` — Missing or invalid API key.

        - `forbidden` — Authenticated but not permitted to perform this action.

        - `not_found` — The requested resource does not exist.

        - `conflict` — Conflicts with current state (e.g. a reused
        Idempotency-Key with a different body).

        - `unsupported_feature` — The composition uses a feature the renderer
        does not support.

        - `invalid_source` — A source URL is malformed or unreachable.

        - `source_not_found` — A referenced source file could not be found.

        - `source_too_large` — A source file exceeds the allowed size.

        - `asset_not_ready` — A referenced asset is still ingesting and is not
        ready yet.

        - `ingest_failed` — Copying an external source into Framelane storage
        failed.

        - `codec_unsupported` — A source uses a codec the renderer cannot
        decode.

        - `quota_exceeded` — The workspace has exhausted its plan quota (HTTP
        402).

        - `rate_limited` — Too many requests; retry after the Retry-After
        interval.

        - `internal` — An unexpected server error occurred.

        - `email_not_verified` — The workspace email is unverified; verify it
        before making authed calls.

        - `email_in_use` — The email is already associated with another
        workspace.

        - `invalid_otp` — The verification code is incorrect or expired.

        - `unsupported_content_type` — The provided content type is not
        supported.

        - `http_error` — A generic HTTP error not covered by a more specific
        code.
      enum:
        - invalid_request
        - unauthorized
        - forbidden
        - not_found
        - conflict
        - unsupported_feature
        - invalid_source
        - source_not_found
        - source_too_large
        - asset_not_ready
        - ingest_failed
        - codec_unsupported
        - quota_exceeded
        - rate_limited
        - internal
        - email_not_verified
        - email_in_use
        - invalid_otp
        - unsupported_content_type
        - http_error
      title: ErrorCode
      type: string
      x-enumDescriptions:
        invalid_request: The request was malformed or failed validation.
        unauthorized: Missing or invalid API key.
        forbidden: Authenticated but not permitted to perform this action.
        not_found: The requested resource does not exist.
        conflict: >-
          Conflicts with current state (e.g. a reused Idempotency-Key with a
          different body).
        unsupported_feature: The composition uses a feature the renderer does not support.
        invalid_source: A source URL is malformed or unreachable.
        source_not_found: A referenced source file could not be found.
        source_too_large: A source file exceeds the allowed size.
        asset_not_ready: A referenced asset is still ingesting and is not ready yet.
        ingest_failed: Copying an external source into Framelane storage failed.
        codec_unsupported: A source uses a codec the renderer cannot decode.
        quota_exceeded: The workspace has exhausted its plan quota (HTTP 402).
        rate_limited: Too many requests; retry after the Retry-After interval.
        internal: An unexpected server error occurred.
        email_not_verified: >-
          The workspace email is unverified; verify it before making authed
          calls.
        email_in_use: The email is already associated with another workspace.
        invalid_otp: The verification code is incorrect or expired.
        unsupported_content_type: The provided content type is not supported.
        http_error: A generic HTTP error not covered by a more specific code.

````