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

# Upload Flow JSON

> Uploads the Flow JSON of a Flow still in `DRAFT`.

**Meta answers `200` with its refusals listed inside the body — this endpoint does not.** `POST /flows/{id}/assets` returns HTTP 200 carrying `validation_errors` when it rejects the document, and a client reading only the status concludes the upload was accepted: the editor shows "saved", the Flow keeps the PREVIOUS JSON, and the divergence surfaces when a customer opens the old form in production. This handler therefore ignores the Graph's status and reads the list: a non-empty list is **422 `FLOW_JSON_INVALID`** with `validationErrors` attached. A `200` from us always carries `validationErrors: []`, and the field is emitted explicitly on success so the client that branches on the status and the one that branches on the list reach the same conclusion.

`flowJson` is a **string**, never a nested object: Meta compiles the exact bytes, and re-serialising would move the positions its errors point at. The list is returned verbatim, untranslated — its shape is Meta's. Body accepts only `flowJson`. Permission `flows:manage`.

**Requires a number-scoped key.** A tenant-scoped key must name the number with the `x-whatsapp-number-id` header, or it gets 403 `TENANT_SCOPE_NOT_ALLOWED`.



## OpenAPI

````yaml openapi.json POST /v1/flows/{id}/json
openapi: 3.1.0
info:
  title: Pilot Status API
  version: 1.0.0
  license:
    name: Pilot Status Terms of Service
    url: https://pilotstatus.com.br/terms
  description: >-
    Public REST API for Pilot Status. Authenticate with the `x-api-key: ps_...`
    header (or `x-api-key-id`). Base URL: https://pilotstatus.com.br
servers:
  - url: https://pilotstatus.com.br
security:
  - apiKey: []
  - apiKeyId: []
paths:
  /v1/flows/{id}/json:
    post:
      tags:
        - Flows
      summary: Upload Flow JSON
      description: >-
        Uploads the Flow JSON of a Flow still in `DRAFT`.


        **Meta answers `200` with its refusals listed inside the body — this
        endpoint does not.** `POST /flows/{id}/assets` returns HTTP 200 carrying
        `validation_errors` when it rejects the document, and a client reading
        only the status concludes the upload was accepted: the editor shows
        "saved", the Flow keeps the PREVIOUS JSON, and the divergence surfaces
        when a customer opens the old form in production. This handler therefore
        ignores the Graph's status and reads the list: a non-empty list is **422
        `FLOW_JSON_INVALID`** with `validationErrors` attached. A `200` from us
        always carries `validationErrors: []`, and the field is emitted
        explicitly on success so the client that branches on the status and the
        one that branches on the list reach the same conclusion.


        `flowJson` is a **string**, never a nested object: Meta compiles the
        exact bytes, and re-serialising would move the positions its errors
        point at. The list is returned verbatim, untranslated — its shape is
        Meta's. Body accepts only `flowJson`. Permission `flows:manage`.


        **Requires a number-scoped key.** A tenant-scoped key must name the
        number with the `x-whatsapp-number-id` header, or it gets 403
        `TENANT_SCOPE_NOT_ALLOWED`.
      operationId: post_flows_id_json
      parameters:
        - name: id
          in: path
          required: true
          description: >-
            The **local** id of the Flow — the `id` field `GET /v1/flows`
            returns, never `metaFlowId`.
          schema:
            type: string
          example: cmf1a2b3c4d5e6f7g8h9i0j1
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - flowJson
              properties:
                flowJson:
                  type: string
                  description: The complete Flow JSON, as a STRING.
                  example: '{"version":"7.1","screens":[]}'
            example:
              flowJson: '{"version":"7.1","screens":[]}'
      responses:
        '200':
          description: Accepted. `validationErrors` is always present and always empty here
          content:
            application/json:
              example:
                id: cmf1a2b3c4d5e6f7g8h9i0j1
                validationErrors: []
        '400':
          description: >-
            `FLOW_JSON_REQUIRED` (the body parsed and `flowJson` is absent or
            empty — distinct from `FLOW_BODY_INVALID`, which means the body did
            not parse), `FLOW_UNKNOWN_FIELDS`, or a Meta refusal
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: Portuguese half
                  errorEN:
                    type: string
                    description: English half
                  code:
                    type: string
              example:
                error: >-
                  Envie um corpo JSON com o campo flowJson: o Flow JSON
                  completo, como STRING.
                errorEN: >-
                  Send a JSON body with a flowJson field: the complete Flow
                  JSON, as a STRING.
                code: FLOW_JSON_REQUIRED
        '401':
          description: Missing or invalid `x-api-key` / `x-api-key-id` header
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  code:
                    type: string
              example:
                error: Unauthorized
        '403':
          description: >-
            Tenant-scoped key used on a number-scoped endpoint, or the key's
            role lacks the permission
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  code:
                    type: string
              example:
                error: Tenant-scoped keys cannot call number endpoints
                code: TENANT_SCOPE_NOT_ALLOWED
        '404':
          description: >-
            No Flow with that id inside the key's own WABA. Never 403: a Flow of
            another tenant is indistinguishable from one that does not exist, on
            purpose — a 403 would confirm someone else's id to whoever guessed
            it
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: Portuguese half
                  errorEN:
                    type: string
                    description: English half
                  code:
                    type: string
              example:
                error: Flow não encontrado para o número desta chave.
                errorEN: Flow not found for this key's number.
                code: FLOW_NOT_FOUND
        '409':
          description: >-
            The Flow has left `DRAFT`. Publishing is irreversible on Meta —
            clone it to start a new version
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: Portuguese half
                  errorEN:
                    type: string
                    description: English half
                  code:
                    type: string
              example:
                error: >-
                  Este Flow já foi publicado na Meta e não pode mais ser
                  alterado. Clone-o para criar uma nova versão.
                errorEN: >-
                  This Flow is already published on Meta and can no longer be
                  changed. Clone it to create a new version.
                code: FLOW_NOT_DRAFT
        '422':
          description: >-
            Meta rejected the document's CONTENT. `validationErrors` carries
            Meta's own list, verbatim. This is the answer to read — not a 200
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  errorEN:
                    type: string
                  code:
                    type: string
                  validationErrors:
                    type: array
                    items: {}
                    description: >-
                      Meta's list, untranslated and unreformatted. Its shape is
                      Meta's and can change without notice.
              example:
                error: >-
                  A Meta recusou este Flow JSON. Veja validationErrors: a lista
                  é a resposta dela, sem tradução.
                errorEN: >-
                  Meta rejected this Flow JSON. See validationErrors: the list
                  is Meta's own answer, untranslated.
                code: FLOW_JSON_INVALID
                validationErrors:
                  - error: INVALID_PROPERTY
                    error_type: JSON_SCHEMA_ERROR
                    message: The property "foo" is not expected
                    line_start: 12
                    line_end: 12
                    column_start: 5
                    column_end: 9
        '429':
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  code:
                    type: string
              example:
                error: Too many requests
        '500':
          description: >-
            Internal error. Never carries the internal message — that belongs in
            the log
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: Portuguese half
                  errorEN:
                    type: string
                    description: English half
                  code:
                    type: string
              example:
                error: Erro interno do servidor.
                errorEN: Internal server error.
                code: INTERNAL_ERROR
components:
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: x-api-key
      description: Your ps_ API key
    apiKeyId:
      type: apiKey
      in: header
      name: x-api-key-id
      description: API key id (alternative to x-api-key)

````