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

# Declare or annotate a variable

> OPTIONAL and non-forcing: attach a note, a declared type hint, or a manual declaration to a variable. Upserting a name that is ALREADY derived returns `200` (never `409`) — the annotation simply attaches to the derived variable. The annotation carries ZERO runtime meaning. Names are stored case-sensitive and must match `^[a-zA-Z_][a-zA-Z0-9_]*$`; the reserved, always-derived names (`caller_info`, `caller_number`, `caller_name`, `verified`) cannot be declared.



## OpenAPI

````yaml /openapi.yaml post /agents/{agentId}/variables
openapi: 3.1.0
info:
  title: Flowyte V2 Control-Plane REST API
  version: 1.0.0
  description: >
    The single REST contract the React builder UI and API-first developers build
    against (FROZEN CONTRACT Part B, schema_version contract.v1). Base path
    `/api/v1`. Dual auth: a Clerk JWT (browser) OR a Flowyte secret API key
    (`flowyte_sk_…`), resolving to one `Principal{org_id, actor_id, actor_type,
    scopes}` and the same `app.current_organization_id` RLS — a key can never
    reach another tenant. A third, browser-only branch authenticates the
    embeddable chat widget via a publishable key (`flowyte_pk_…`, scope
    `chat:public`, origin-allowlisted, agent-pinned).


    Success bodies are the `ApiResponse<T>` envelope; list bodies are
    `PaginatedResponse<T>`. Error bodies are RFC 9457 problem+json
    (`application/problem+json`) with the `ApiResponse` envelope still populated
    on validation errors. SSE endpoints stream `text/event-stream`
    (`event:<type>\ndata:<json>\n\n`; terminal frame `event: done` or `event:
    error`) and are consumed via `fetch()` streaming so the Clerk
    `Authorization` + `X-Organization-Id` headers attach.
  contact:
    name: Flowyte Platform
  license:
    name: Proprietary
servers:
  - url: /api/v1
    description: Flowyte control-plane (versioned URI; additive in v1).
security:
  - clerkJwt: []
  - apiKey: []
tags:
  - name: Agents
    description: The single user-facing entity (B.1).
  - name: Support
    description: In-app "Get help" form → support inbox email.
  - name: Skills
    description: >
      Agent capabilities / tools (B.2). A skill is ONE atomic action — typically
      a single API call the agent makes in one step (look up an order, create a
      record, send a message, transfer the call). Use a skill when the task is a
      single step. When a task needs several details gathered across turns
      BEFORE acting, build a Playbook (which gathers the inputs and then calls
      skills) — see the Playbooks tag.
  - name: Integrations
    description: Native OAuth integrations (B.2).
  - name: Knowledge
    description: RAG knowledge sources & preview (B.3).
  - name: Playbooks
    description: >
      Multi-turn conversation scripts — a node graph (gather → confirm → branch)
      the agent follows to collect several inputs IN ORDER across turns before
      acting (B.4). Build a playbook when a single skill call isn't enough
      because the agent must gather MANY details first, or run a SEQUENCE of
      skills, before it can finish (e.g. take a full service request: gather the
      problem, address, and time, confirm, THEN file it; qualify a lead; a
      multi-step intake). A playbook does NOT call an integration itself — it
      owns the conversation and holds the state across turns; the actual action
      is performed by the SKILL(s) it gathers the inputs for. So a playbook
      ORCHESTRATES skills. Rule of thumb — one API call → a Skill; "gather N
      things in order, then submit" → a Playbook that drives the conversation
      and calls the skill(s) at the end.
  - name: Variables
    description: >
      The agent-wide interaction-variable registry (B.4b) — DERIVED at read time
      from the agent's playbooks and skills (collect slots, skill output
      bindings, {var} placeholders) and merged with a thin annotation overlay
      (notes, declared type hints, manual declarations). Read-only observation,
      NEVER a gate: it never validates a reference and never affects authoring,
      publish, or runtime behaviour.
  - name: Guardrails
    description: Deterministic guardrail policies & caller verification (B.5).
  - name: Numbers
    description: Phone numbers / DIDs (B.6).
  - name: Test
    description: Test, simulate, talk-token, probe (B.7).
  - name: Observe
    description: Post-call analytics, conversations, receipts, transcripts (B.8).
  - name: Billing
    description: Plans, wallet, usage, fixed phrases (B.9).
  - name: Voices
    description: Voice catalog (B.10).
  - name: ApiKeys
    description: Developer / API keys (B.11).
  - name: Webhooks
    description: Webhook endpoints & deliveries (B.12).
  - name: Records
    description: >
      Caller Context Store (Zapier Integration Plan, Lane C) — pre-synced
      external records the agent greets a caller from, plus the learned
      object-type field registry. Fed by the Zapier app's Create/Update Record
      action and directly by this REST API; read at call time by the
      context_lookup skill (a sub-100ms local lookup, no Zapier round-trip).
  - name: AuditLogs
    description: API/key activity logs (B.13).
  - name: Chat
    description: Chat channel — sessions, messages, OpenAI-compatible, widget (B.14).
  - name: PublishableKeys
    description: Browser-safe, one-agent publishable keys (B.15).
  - name: Widget
    description: Embed widget config + snippet (B.16).
  - name: Uploads
    description: Multipart file uploads backing file_id params (Addenda
  - name: Meta
    description: Platform metadata (language/SKU/tier capabilities).
  - name: Outbound
    description: >
      Outbound voice — contact lists (import + scrub) and campaigns (create,
      launch, pause/resume/cancel). Launch schedules one call attempt per valid
      contact; a background worker dials them. This release dials informational
      campaigns only.
  - name: OAuth2
    description: >
      Flowyte's minimal OAuth2 authorization server (Zapier Integration Plan,
      Wave 2 Track B). The Flowyte Zapier app is the first registered client.
      The authorization-code grant (confidential client, optional PKCE) issues
      org-scoped SERVICE tokens (flowyte_oat_…) that ride the SAME scope matrix
      as an sk key. The public authorize/token/revoke endpoints are served at
      the ORIGIN ROOT (NOT under /api/v1 — see each path's `servers` override);
      consent + disconnect are /api/v1.
paths:
  /agents/{agentId}/variables:
    parameters:
      - $ref: '#/components/parameters/AgentIdPathScoped'
    post:
      tags:
        - Variables
      summary: Declare or annotate a variable
      description: >-
        OPTIONAL and non-forcing: attach a note, a declared type hint, or a
        manual declaration to a variable. Upserting a name that is ALREADY
        derived returns `200` (never `409`) — the annotation simply attaches to
        the derived variable. The annotation carries ZERO runtime meaning. Names
        are stored case-sensitive and must match `^[a-zA-Z_][a-zA-Z0-9_]*$`; the
        reserved, always-derived names (`caller_info`, `caller_number`,
        `caller_name`, `verified`) cannot be declared.
      operationId: declareVariable
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VariableDeclare'
      responses:
        '200':
          description: The upserted variable (merged derived ∪ annotation).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponse_Variable'
        '400':
          $ref: '#/components/responses/ValidationError'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
      security:
        - clerkJwt: []
        - apiKey:
            - playbooks:write
components:
  parameters:
    AgentIdPathScoped:
      name: agentId
      in: path
      required: true
      schema:
        type: string
  schemas:
    VariableDeclare:
      type: object
      description: >-
        Declare or annotate a variable. Upserting an already-derived name is a
        200, not a conflict.
      required:
        - name
      properties:
        name:
          type: string
          description: >-
            The variable name. Stored case-sensitive; must match
            ^[a-zA-Z_][a-zA-Z0-9_]*$. The reserved names (caller_info,
            caller_number, caller_name, verified) cannot be declared.
        note:
          type: string
        type:
          type: string
          description: An optional, never-enforced type hint.
    ApiResponse_Variable:
      allOf:
        - $ref: '#/components/schemas/ApiResponseBase'
        - type: object
          required:
            - data
          properties:
            data:
              $ref: '#/components/schemas/Variable'
    ApiResponseBase:
      type: object
      required:
        - success
      properties:
        success:
          type: boolean
        message:
          type: string
        errors:
          type: array
          items:
            type: object
            required:
              - field
              - message
            properties:
              field:
                type: string
              message:
                type: string
    Variable:
      type: object
      description: >-
        A merged interaction variable: the DERIVED registry (scanned from
        playbooks + skills) unioned with the per-agent annotation overlay.
        `source`, `deletable`, `producedBy`, and `consumedBy` are computed from
        the scan; `note`, `type`, and `updatedAt` come from the annotation.
        `type` is an OPTIONAL, nullable hint that is NEVER enforced.
      required:
        - name
        - source
        - producedBy
        - consumedBy
        - note
        - deletable
      properties:
        name:
          type: string
          description: The variable name (case-sensitive identifier).
        source:
          type: string
          description: derived (graph-only) | declared (annotation-only) | both.
          enum:
            - derived
            - declared
            - both
        type:
          type:
            - string
            - 'null'
          description: An optional, never-enforced type hint from the annotation.
        producedBy:
          type: array
          description: The deduped set of origins that PRODUCE this variable.
          items:
            $ref: '#/components/schemas/VariableOrigin'
        consumedBy:
          type: array
          description: The deduped set of origins that CONSUME this variable.
          items:
            $ref: '#/components/schemas/VariableOrigin'
        note:
          type: string
          description: The annotation note (empty when there is no annotation).
        deletable:
          type: boolean
          description: >-
            true only when the variable has NO producers AND NO consumers (a
            purely-declared annotation). A still-referenced name reappears as
            pure-derived even if its annotation is dropped, so it is not
            deletable.
        updatedAt:
          type: string
          format: date-time
          description: >-
            When the annotation was last edited (omitted when there is no
            annotation).
    ProblemDetails:
      description: RFC 9457 problem+json.
      type: object
      properties:
        type:
          type: string
          format: uri
          default: about:blank
        title:
          type: string
        status:
          type: integer
        detail:
          type: string
        instance:
          type: string
        code:
          type: string
        errors:
          type: array
          items:
            type: object
            properties:
              field:
                type: string
              message:
                type: string
    ApiResponse_Void:
      allOf:
        - $ref: '#/components/schemas/ApiResponseBase'
        - type: object
          properties:
            data:
              type:
                - object
                - 'null'
    VariableOrigin:
      type: object
      description: >-
        One producer or consumer of a variable — WHERE (ref) and HOW (kind) it
        appears.
      required:
        - kind
        - ref
      properties:
        kind:
          type: string
          description: >-
            The mechanism this origin represents. Producers: collect (a playbook
            collect slot), skill_output (a skill output binding). Consumers:
            message (a {var} in a message), binding (a {var} in a skill node's
            args), branch (a slot a branch routes on), skill_config (a {var} in
            a standalone skill's config).
          enum:
            - collect
            - skill_output
            - message
            - binding
            - branch
            - skill_config
        ref:
          type: string
          description: >-
            A human-readable location, e.g. "Book a visit · Collect" or "Skill
            send_email".
  responses:
    ValidationError:
      description: Validation failure (errors[] populated on the envelope).
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
        application/json:
          schema:
            $ref: '#/components/schemas/ApiResponse_Void'
    NotFound:
      description: Resource not found in org scope.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
    Unprocessable:
      description: Semantically invalid (e.g. language not in SKU's STT set).
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
  securitySchemes:
    clerkJwt:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >
        Clerk JWT (browser). Requires `Authorization: Bearer <clerk_jwt>` and
        the `X-Organization-Id` header. Validated by the ported Clerk
        middleware, which sets the `app.current_organization_id` RLS context.
    apiKey:
      type: oauth2
      description: >
        Flowyte secret API key (`Authorization: Bearer flowyte_sk_live_…`).
        Scope-gated; resolves to the same per-org RLS as a Clerk JWT — a key can
        never reach another tenant. The listed scopes in each operation's
        `apiKey` requirement are the scopes that key must hold. The `tokenUrl`
        is nominal: keys are minted via `POST /api-keys`, not an OAuth flow.
      flows:
        clientCredentials:
          tokenUrl: /api/v1/api-keys
          scopes:
            agents:read: Read agents.
            agents:write: Create/update/delete agents, publish, rollback.
            knowledge:read: Read knowledge sources & preview.
            knowledge:write: Add/remove knowledge sources, uploads.
            skills:read: Read skills & skill-types.
            skills:write: Create/update/delete skills.
            playbooks:read: Read playbooks & graphs.
            playbooks:write: Create/update/delete playbooks & graphs.
            guardrails:read: Read guardrail policies & caller-verification.
            guardrails:write: Update guardrail policies & caller-verification.
            numbers:read: Read phone numbers / search availability.
            numbers:write: Purchase / assign / release numbers.
            sms:read: Read the org's SMS (10DLC) registration status and numbers.
            sms:write: Save/submit the 10DLC registration and toggle numbers for SMS.
            outbound:read: Read outbound contact lists and campaigns.
            outbound:write: >-
              Create/import contact lists, create/launch/pause/resume/cancel
              outbound campaigns, and enqueue single outbound calls.
            integrations:read: Read connected native integrations (status only — never tokens).
            integrations:write: Discover schemas, set data scoping, and disconnect a connection.
            integrations:connect: >-
              Connect a data source (submit credentials / begin OAuth) — a
              SEPARATE, higher-privilege scope because connecting INGESTS
              credentials and opens a new egress path; a discover/scope/author
              key need not carry it.
            records:read: >-
              Read the pre-synced Caller Context Store records and object-type
              field registry.
            records:write: >-
              Upsert / delete / bulk-import / purge caller-context records and
              edit type metadata.
            calls:read: Read conversations, receipts, transcripts, analytics.
            analytics:read: >-
              Read the Observe history list, per-agent analytics (the
              answer-first money card), and the raw knowledge-gap list.
            analytics:write: >-
              Curate knowledge gaps (dismiss / mark in-progress). [M4 —
              reserved]
            billing:read: Read plans, wallet, usage.
            audit:read: Read API/key activity logs.
            webhooks:write: Manage webhook endpoints.
            keys:write: Manage secret API keys.
            chat:read: Read chat sessions & messages.
            chat:write: Create chat sessions & send messages (server-side).
            widgets:read: Read widget config & embed snippet.
            widgets:write: Update widget config.
            pubkeys:read: Read publishable keys.
            pubkeys:write: Manage publishable keys.

````