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

# Call a gateway operation of an adapter a published widget uses

> The door a widget's adapter module calls, from the visitor's browser, for the operations that cannot run in the browser: anything with a secret, a charge in credits or the owner's storage behind it. A widget that uses such an adapter is handed the address of this route as `adapters.url` in its `widgetic:host` message (see the host bridge contract) and calls `{adapters.url}/{adapterId}/{operation}`. It is not meant to be called by hand, and it takes no API key.
**Address.** `https://api.widgetic.com/embed/compositions/{compositionId}/adapters/{adapterId}/{operation}`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.
**What it serves.** Only an adapter, and a version of it, that the widget's LIVE manifest (what the version that is published uses) lists, and only an operation that runs on the gateway. Anything else is one 404 `ADAPTER_NOT_AVAILABLE`, whatever the reason, so the route is no way to ask what a widget contains. An adapter an administrator stopped offering to NEW widgets keeps serving the widgets that already use it.
**Who may call.** The same rule as the entry route: a permitted `Origin` header, which is the artifact origin the widget runs from or any website when the owner has not restricted the allowed websites (when the owner has, one of the websites the owner listed, Widgetic's own sites, the visitor's own machine or the owner's own platform). Page script cannot forge it. A request without a permitted `Origin` is 403 `ORIGIN_NOT_ALLOWED`. A composition that is not served (unpublished, or its owner's plan does not serve it) is 404, with the codes of the other public embed routes. When the partner of the owner signs the host context, a context that is not signed as the partner requires is refused with 403 `CONTEXT_SIGNATURE_REQUIRED`, `CONTEXT_SIGNATURE_INVALID` or `CONTEXT_SIGNATURE_EXPIRED`.
**Cost and retries.** An operation priced in credits is charged to the owner of the widget (for a customer a partner manages, to the partner's administrator, within the monthly credits set for that customer), once for each `requestId`, before it runs, and given back if it does not answer. A `requestId` that was charged and not given back is 409 `REQUEST_ALREADY_USED` and the operation is NOT run a second time; one whose charge was given back may be tried again with the same `requestId`. A visitor never sees an owner's balance: a feature that cannot be used because its owner has no credits left, because the customer's monthly credits are spent, or because an administrator stopped the adapter, is the same 503 `ADAPTER_UNAVAILABLE` as every other "not right now". The owner is told through a notice.
**Limits.** In front of everything, 120 calls a minute for each visitor address and composition and 600 a minute for a composition (429 `RATE_LIMITED`). Then, for each operation of a widget, a rate for each visitor address a minute and a cap for the widget a day: 30 and 5000 for an operation that is free, 10 and 500 for one that costs credits, unless the adapter says otherwise (never above 120 and 100000). A visitor over the rate is 429 `RATE_LIMITED`; a widget over its cap is 429 `WIDGET_LIMIT_REACHED`. Both carry a `Retry-After` header. The body is at most 64 KiB (413 `PAYLOAD_TOO_LARGE`) and JSON (415 `UNSUPPORTED_MEDIA_TYPE`); an operation has 30 seconds to answer (504 `ADAPTER_TIMEOUT`, and the charge is given back).



## OpenAPI

````yaml /openapi/widgetic-api-public.json post /embed/compositions/{compositionId}/adapters/{adapterId}/{operation}
openapi: 3.0.0
info:
  title: Widgetic API Gateway
  version: 1.0.0
  description: API documentation for the Widgetic API Gateway
  license:
    name: Widgetic Integration License
    url: https://docs.widgetic.com/resources/license
servers:
  - url: https://api.widgetic.com/v1
    description: Production server
    variables:
      env:
        default: production
        description: Environment
      apiUrl:
        default: https://api.widgetic.com
        description: API URL
      apiVersion:
        default: /v1
        description: API Version path prefix
security: []
tags: []
paths:
  /embed/compositions/{compositionId}/adapters/{adapterId}/{operation}:
    servers:
      - url: https://api.widgetic.com
        description: >-
          The public embed routes are served at the root of the API host, not
          under /v1.
    post:
      tags:
        - Public Embed
      summary: Call a gateway operation of an adapter a published widget uses
      description: >-
        The door a widget's adapter module calls, from the visitor's browser,
        for the operations that cannot run in the browser: anything with a
        secret, a charge in credits or the owner's storage behind it. A widget
        that uses such an adapter is handed the address of this route as
        `adapters.url` in its `widgetic:host` message (see the host bridge
        contract) and calls `{adapters.url}/{adapterId}/{operation}`. It is not
        meant to be called by hand, and it takes no API key.

        **Address.**
        `https://api.widgetic.com/embed/compositions/{compositionId}/adapters/{adapterId}/{operation}`:
        the root of the API host, NOT under `/v1`. A client generated from this
        description for `https://api.widgetic.com/v1` must be given the base
        address `https://api.widgetic.com` for the calls of the `Public Embed`
        tag.

        **What it serves.** Only an adapter, and a version of it, that the
        widget's LIVE manifest (what the version that is published uses) lists,
        and only an operation that runs on the gateway. Anything else is one 404
        `ADAPTER_NOT_AVAILABLE`, whatever the reason, so the route is no way to
        ask what a widget contains. An adapter an administrator stopped offering
        to NEW widgets keeps serving the widgets that already use it.

        **Who may call.** The same rule as the entry route: a permitted `Origin`
        header, which is the artifact origin the widget runs from or any website
        when the owner has not restricted the allowed websites (when the owner
        has, one of the websites the owner listed, Widgetic's own sites, the
        visitor's own machine or the owner's own platform). Page script cannot
        forge it. A request without a permitted `Origin` is 403
        `ORIGIN_NOT_ALLOWED`. A composition that is not served (unpublished, or
        its owner's plan does not serve it) is 404, with the codes of the other
        public embed routes. When the partner of the owner signs the host
        context, a context that is not signed as the partner requires is refused
        with 403 `CONTEXT_SIGNATURE_REQUIRED`, `CONTEXT_SIGNATURE_INVALID` or
        `CONTEXT_SIGNATURE_EXPIRED`.

        **Cost and retries.** An operation priced in credits is charged to the
        owner of the widget (for a customer a partner manages, to the partner's
        administrator, within the monthly credits set for that customer), once
        for each `requestId`, before it runs, and given back if it does not
        answer. A `requestId` that was charged and not given back is 409
        `REQUEST_ALREADY_USED` and the operation is NOT run a second time; one
        whose charge was given back may be tried again with the same
        `requestId`. A visitor never sees an owner's balance: a feature that
        cannot be used because its owner has no credits left, because the
        customer's monthly credits are spent, or because an administrator
        stopped the adapter, is the same 503 `ADAPTER_UNAVAILABLE` as every
        other "not right now". The owner is told through a notice.

        **Limits.** In front of everything, 120 calls a minute for each visitor
        address and composition and 600 a minute for a composition (429
        `RATE_LIMITED`). Then, for each operation of a widget, a rate for each
        visitor address a minute and a cap for the widget a day: 30 and 5000 for
        an operation that is free, 10 and 500 for one that costs credits, unless
        the adapter says otherwise (never above 120 and 100000). A visitor over
        the rate is 429 `RATE_LIMITED`; a widget over its cap is 429
        `WIDGET_LIMIT_REACHED`. Both carry a `Retry-After` header. The body is
        at most 64 KiB (413 `PAYLOAD_TOO_LARGE`) and JSON (415
        `UNSUPPORTED_MEDIA_TYPE`); an operation has 30 seconds to answer (504
        `ADAPTER_TIMEOUT`, and the charge is given back).
      operationId: callPublicCompositionAdapter
      parameters:
        - in: path
          name: compositionId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: adapterId
          required: true
          description: >-
            The id of the adapter, the name its module is known by, for example
            `upload`.
          schema:
            type: string
            maxLength: 40
            pattern: ^[a-z][a-z0-9]*(-[a-z0-9]+)*$
        - in: path
          name: operation
          required: true
          description: >-
            The name of one operation of the adapter, as its documentation gives
            it.
          schema:
            type: string
            maxLength: 40
            pattern: ^[a-z][a-z0-9]*(-[a-z0-9]+)*$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PublicAdapterCallRequest'
      responses:
        '200':
          description: The operation answered
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicAdapterCallResult'
        '400':
          description: >-
            The body is not a call (`ADAPTER_BODY_INVALID`: a field that is not
            named here, a `requestId` of another shape, an `input` that is not
            an object), or the operation refused its input with a code of its
            own.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicAdapterCallError'
        '403':
          description: >-
            Origin not allowed (`ORIGIN_NOT_ALLOWED`), the operation asks for a
            CAPTCHA that is missing or failed (`CAPTCHA_REQUIRED`), or the
            context is not signed as the partner requires
            (`CONTEXT_SIGNATURE_REQUIRED`, `CONTEXT_SIGNATURE_INVALID`,
            `CONTEXT_SIGNATURE_EXPIRED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicAdapterCallError'
        '404':
          description: >-
            The widget does not use this adapter, this version or this
            operation, or its owner switched it off in the editor
            (`ADAPTER_NOT_AVAILABLE`), or the composition is not there or not
            served (`NOT_FOUND`, `UNPUBLISHED`, `SUSPENDED`; an id that is not a
            UUID is `NOT_FOUND` too).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicAdapterCallError'
        '409':
          description: >-
            This `requestId` was already charged and not given back
            (`REQUEST_ALREADY_USED`). Start a new request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicAdapterCallError'
        '413':
          description: >-
            Body too large (`PAYLOAD_TOO_LARGE`), or the operation refused an
            input it cannot take
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicAdapterCallError'
        '415':
          description: Not JSON (`UNSUPPORTED_MEDIA_TYPE`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicAdapterCallError'
        '422':
          description: >-
            The operation refused an input it understood and cannot use, with a
            code of its own
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicAdapterCallError'
        '429':
          description: >-
            A limit was reached (`code` is `RATE_LIMITED`, or
            `WIDGET_LIMIT_REACHED` for the widget's daily cap); the
            `Retry-After` header says when to ask again. The body is the
            standard rate-limit envelope with the flat `code` beside it.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicEmbedFailure'
        '502':
          description: >-
            The operation failed (`ADAPTER_FAILED`, or a code of its own); any
            charge was given back
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicAdapterCallError'
        '503':
          description: >-
            The feature cannot be used right now (`ADAPTER_UNAVAILABLE`; the
            cause is never said), or the gateway could not tell whether the
            context must be signed (`CONTEXT_SIGNATURE_UNAVAILABLE`). Try again
            later.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicAdapterCallError'
        '504':
          description: >-
            The operation took too long (`ADAPTER_TIMEOUT`); any charge was
            given back
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicAdapterCallError'
components:
  schemas:
    PublicAdapterCallRequest:
      type: object
      required:
        - requestId
      additionalProperties: false
      properties:
        requestId:
          type: string
          pattern: ^[A-Za-z0-9][A-Za-z0-9_-]{7,63}$
          description: >-
            Names this attempt: 8 to 64 characters, letters, digits, `_` and
            `-`, the first a letter or a digit (a UUID fits). A module makes a
            new one for each action of the visitor. An operation that costs
            credits is charged once for each `requestId`, so a repeat of one
            that was charged and not given back is refused (409
            `REQUEST_ALREADY_USED`) and does no work.
        version:
          type: integer
          minimum: 1
          maximum: 1000
          description: >-
            The version of the adapter the widget's code was built against (the
            `vN` of its import). When it is left out, the newest version the
            widget's live manifest lists.
        input:
          type: object
          additionalProperties: true
          description: >-
            What the operation is asked to do. Each operation says what it
            accepts and answers 400, 413 or 422 with a code of its own for what
            it cannot use. Defaults to an empty object.
        context:
          type: object
          nullable: true
          additionalProperties: true
          description: >-
            The host context the embedding page gave the widget, as the entry
            route takes it (see the description of
            `createPublicCompositionEntry`, with its **Signed context**). It is
            not authenticated unless the partner signs it. At most 4096 bytes of
            JSON, the two signature keys included.
        captchaToken:
          type: string
          nullable: true
          maxLength: 4096
          description: >-
            The visitor's CAPTCHA token, for an operation that asks for one (the
            answer `CAPTCHA_REQUIRED` says so).
      example:
        requestId: Example requestId
        version: 42
        input: {}
        context: {}
        captchaToken: Example captchaToken
    PublicAdapterCallResult:
      type: object
      required:
        - success
        - data
      properties:
        success:
          type: boolean
          example: true
        data:
          type: object
          required:
            - output
          properties:
            output:
              type: object
              additionalProperties: true
              description: >-
                What the operation answered, exactly as it wrote it: its field
                names are NOT rewritten to camelCase on the way out. At most 256
                KiB of JSON. Each operation documents its own fields.
      example:
        success: true
        data: {}
    PublicAdapterCallError:
      type: object
      required:
        - success
        - error
        - code
      properties:
        success:
          type: boolean
          example: false
        error:
          type: string
          description: >-
            A sentence a visitor may read. It never says why a feature is
            unavailable: not that its owner is out of credits, not what a
            balance is, not which limit was reached beyond the word
            `RATE_LIMITED` or `WIDGET_LIMIT_REACHED`. (On a 429 the standard
            rate-limit envelope is sent instead: `error` is then an object, and
            `code` beside it is the flat code.)
        code:
          type: string
          description: >-
            What a module switches on. The codes of the gateway itself are
            `ADAPTER_BODY_INVALID`, `CAPTCHA_REQUIRED`, `ADAPTER_NOT_AVAILABLE`,
            `REQUEST_ALREADY_USED`, `RATE_LIMITED`, `WIDGET_LIMIT_REACHED`,
            `ADAPTER_FAILED`, `ADAPTER_UNAVAILABLE` and `ADAPTER_TIMEOUT`, with
            `ORIGIN_NOT_ALLOWED`, `PAYLOAD_TOO_LARGE`, `UNSUPPORTED_MEDIA_TYPE`,
            `CONTEXT_SIGNATURE_REQUIRED`, `CONTEXT_SIGNATURE_INVALID`,
            `CONTEXT_SIGNATURE_EXPIRED`, `CONTEXT_SIGNATURE_UNAVAILABLE` and the
            codes of the serving gate (`NOT_FOUND`, `UNPUBLISHED`, `SUSPENDED`)
            it shares with the other public embed routes. An operation may add
            codes of its own (capital letters and `_`) for an input it refused.
        correlationId:
          type: string
          description: Quote it when asking for help with a call.
      example:
        success: false
        error: Example error
        code: Example code
        correlationId: Example correlationId
    PublicEmbedFailure:
      type: object
      required:
        - success
        - message
        - error
        - code
      description: >-
        What a public embed route answers when it refuses or fails. It is the
        standard error answer of the API (`success`, `message`, `error`), and
        the short `code` (with `details` and `correlationId` when the route has
        them) is also at the top level, because a page that embeds a composition
        branches on it there. `error.code` is the same word in the codes of the
        rest of the API where those have it: `NOT_FOUND` is
        `RESOURCE_NOT_FOUND`, `RATE_LIMITED` is `RATE_LIMIT_EXCEEDED`, and the
        others (`DOMAIN_NOT_ALLOWED`, `UNPUBLISHED`, `SUSPENDED`, the codes of
        the entry route) are the same in both places.
      properties:
        success:
          type: boolean
          enum:
            - false
        message:
          type: string
          description: What happened, for a person.
        code:
          type: string
          description: >-
            The short code a page branches on: `DOMAIN_NOT_ALLOWED`,
            `UNPUBLISHED`, `SUSPENDED`, `NOT_FOUND`, `RATE_LIMITED`, and for the
            entry route the codes listed with its answers.
          example: NOT_FOUND
        details:
          type: object
          additionalProperties: true
          description: >-
            What tells the cases of one code apart, such as `reason` of
            `DOMAIN_NOT_ALLOWED`.
          properties:
            reason:
              type: string
              description: For `DOMAIN_NOT_ALLOWED`, why the website was refused.
              enum:
                - domain_not_allowed
                - opaque_origin
            hasDomainsConfigured:
              type: boolean
              description: >-
                For `DOMAIN_NOT_ALLOWED`, always true (an owner who listed
                nothing is never refused).
        correlationId:
          type: string
          description: The id of the request, for the routes that carry it.
        error:
          type: object
          required:
            - code
            - message
            - timestamp
          properties:
            code:
              type: string
              example: RESOURCE_NOT_FOUND
            message:
              type: string
            details:
              type: object
              additionalProperties: true
            correlationId:
              type: string
            retryAfter:
              type: integer
              description: Seconds to wait before asking again, for a rate-limit refusal.
            timestamp:
              type: string
              format: date-time
      example:
        success: true
        message: Example message
        code: NOT_FOUND
        details: {}
        correlationId: Example correlationId
        error: {}

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.