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

# Create an API key

> Makes a new API key for an account and returns its secret in `key`. That is the only time the secret is shown: Widgetic keeps a hash, so a key that was not stored cannot be recovered, only replaced. To rotate a key, make the new one, switch the integration over to it, then delete the old one.
Only a person signed in on widgetic.com can make a key: a request made with an API key, or with an SDK token, is refused with 403. That way a leaked key cannot be used to make a second one that survives its revocation. Make keys from the dashboard; a key can still list, rename, switch off and delete keys.
An `Idempotency-Key` header is required (8 to 50 characters: letters, digits, `-` and `_`). A retry with the same header and the same body returns the same answer instead of making a second key, and the same header with a different body is refused with 422. Make keys one at a time: two requests for the same account that run at the same moment can overwrite each other.
A key can be limited to what it needs. Send a `preset` (`read_only`, `content` or `full_access`) or a list of `scopes`, never both. Send neither for a key that can do everything its owner can, as every key made before scopes existed. A limited key is refused with 403 and the code `API_KEY_SCOPE_MISSING` on anything outside its scopes, so a key held by a build server or by a partner's backend can be given only the part of the API it uses. The scopes of a key are fixed when it is made: to widen one, make a new key and delete the old one. A limited key cannot make, change or delete keys, exchange tokens or reach the administration routes. The list of scopes is in the `ApiKeyScope` schema.
Accounts with API access (Solo, Teams and partner accounts) have no limit on how many keys they hold. An account on a plan without API access cannot make one (a partner account is not asked about a plan).



## OpenAPI

````yaml /openapi/widgetic-api-public.json post /users/{id}/keys
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:
  /users/{id}/keys:
    post:
      tags:
        - Users
      summary: Create an API key
      description: >-
        Makes a new API key for an account and returns its secret in `key`. That
        is the only time the secret is shown: Widgetic keeps a hash, so a key
        that was not stored cannot be recovered, only replaced. To rotate a key,
        make the new one, switch the integration over to it, then delete the old
        one.

        Only a person signed in on widgetic.com can make a key: a request made
        with an API key, or with an SDK token, is refused with 403. That way a
        leaked key cannot be used to make a second one that survives its
        revocation. Make keys from the dashboard; a key can still list, rename,
        switch off and delete keys.

        An `Idempotency-Key` header is required (8 to 50 characters: letters,
        digits, `-` and `_`). A retry with the same header and the same body
        returns the same answer instead of making a second key, and the same
        header with a different body is refused with 422. Make keys one at a
        time: two requests for the same account that run at the same moment can
        overwrite each other.

        A key can be limited to what it needs. Send a `preset` (`read_only`,
        `content` or `full_access`) or a list of `scopes`, never both. Send
        neither for a key that can do everything its owner can, as every key
        made before scopes existed. A limited key is refused with 403 and the
        code `API_KEY_SCOPE_MISSING` on anything outside its scopes, so a key
        held by a build server or by a partner's backend can be given only the
        part of the API it uses. The scopes of a key are fixed when it is made:
        to widen one, make a new key and delete the old one. A limited key
        cannot make, change or delete keys, exchange tokens or reach the
        administration routes. The list of scopes is in the `ApiKeyScope`
        schema.

        Accounts with API access (Solo, Teams and partner accounts) have no
        limit on how many keys they hold. An account on a plan without API
        access cannot make one (a partner account is not asked about a plan).
      operationId: createUserApiKey
      parameters:
        - name: id
          in: path
          required: true
          description: The account the key is for. For your own, your own user id
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKeyRequired'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUserApiKeyRequest'
      responses:
        '200':
          description: The key was made. The secret is in `key`, and is not shown again
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreatedUserApiKeyResponse'
        '400':
          description: >-
            The name, the expiry or the scopes are not valid (an unknown scope,
            an empty list, or both a preset and scopes), or the Idempotency-Key
            header is missing or malformed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Not authenticated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: >-
            The request was made with an API key or an SDK token (only a person
            signed in on widgetic.com can make a key), the account belongs to
            someone else, or its plan does not include API keys
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Another request with this Idempotency-Key is still running
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: This Idempotency-Key was already used with a different body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - apiKeyAuth: []
components:
  parameters:
    IdempotencyKeyRequired:
      in: header
      name: Idempotency-Key
      required: true
      description: >-
        Required: a call without it is refused with `400`. A key of your own
        that names this one request; a UUID is ideal. It is 8 to 50 characters
        of letters, digits, `-` and `_`. Send the same key again when you retry
        (after a timeout, a dropped connection or a `5xx`) and the call is
        carried out once: the retry gets the first answer back, marked
        `X-Idempotency-Cached: true`. The same key with a different body is
        refused with `422`, and a retry that arrives while the first call is
        still running with `409` (see `Retry-After`).
      schema:
        type: string
        minLength: 8
        maxLength: 50
        pattern: ^[a-zA-Z0-9_-]{8,50}$
        example: 9b2d6f0a-3c41-4e5a-8d77-1f2e3a4b5c6d
  schemas:
    CreateUserApiKeyRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
          description: A name for the key, to tell it from the account's other keys
          example: Production
        expiresAt:
          type: string
          format: date-time
          description: >-
            When the key stops working, as a date and time in the future with a
            zone. Omit it for a key that does not expire
          example: '2027-01-31T00:00:00Z'
        preset:
          type: string
          enum:
            - read_only
            - content
            - full_access
          description: >-
            A ready-made list of scopes. `read_only` looks at everything the
            account holds and changes nothing. `content` builds and manages
            widgets and compositions and runs the AI, and touches no billing, no
            people and no partners. `full_access` is the same as sending neither
            `preset` nor `scopes`. Send either `preset` or `scopes`, not both
          example: content
        scopes:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/ApiKeyScope'
          description: >-
            What the key may do, when a preset is not enough. Naming a write
            scope includes its read scope, and a list that holds every scope is
            a key with full access. Send either `scopes` or `preset`, not both
          example:
            - content:read
            - generate
      required:
        - name
      example:
        name: Production
        expiresAt: '2027-01-31T00:00:00Z'
        preset: content
        scopes:
          - content:read
          - generate
    CreatedUserApiKeyResponse:
      type: object
      required:
        - success
        - data
      properties:
        success:
          type: boolean
          description: Always `true` on a success.
          example: true
        data:
          $ref: '#/components/schemas/CreatedUserApiKey'
        meta:
          $ref: '#/components/schemas/ResponseMeta'
    ErrorResponse:
      type: object
      required:
        - success
        - message
        - error
      additionalProperties: true
      properties:
        success:
          type: boolean
          description: Always `false` on a failure.
          example: false
        message:
          type: string
          description: The same text as `error.message`, for a caller that reads one line.
        error:
          $ref: '#/components/schemas/ErrorDetail'
    ApiKeyScope:
      type: string
      enum:
        - content:read
        - content:write
        - generate
        - partner:read
        - partner:write
        - billing:read
        - billing:write
        - account:read
        - account:write
      description: >-
        One thing an API key may be allowed to do. A key made with a list of
        scopes does only what the list

        names; a key made without one has full access, and does everything its
        owner can.


        - `content:read` reads widgets, compositions, canvases, projects,
        folders, uploads, translations and the
          analytics of your content.
        - `content:write` creates, changes, publishes and deletes the same. It
        includes `content:read`.

        - `generate` runs the AI that builds widgets and compositions, and
        follows or cancels what it is doing.
          This spends credits.
        - `partner:read` reads managed users, what they spent, presets, the
        contract and the storage destination.

        - `partner:write` creates, changes and deletes managed users and
        presets. It includes `partner:read`.

        - `billing:read` reads credits, usage, invoices, subscriptions and
        payment methods.

        - `billing:write` buys credits, pays invoices and changes a subscription
        or a payment method. It
          includes `billing:read`.
        - `account:read` reads the profile, organizations and members, allowed
        domains, notices and the names
          of API keys.
        - `account:write` changes the profile, organizations and members,
        allowed domains and integrations. It
          includes `account:read`.

        Starting an editor session for a managed user needs `partner:write`,
        `content:write` and `generate`

        together. Making, changing and deleting keys, exchanging tokens, signing
        in and the administration

        routes need full access: a limited key cannot make itself a bigger one.


        A request outside the scopes of the key is refused with 403 and the code
        `API_KEY_SCOPE_MISSING`.

        `error.details.missingScopes` names what a new key needs, and
        `error.details.fullAccessRequired` is true

        when no list of scopes would have been enough. The scopes of a key
        cannot be changed once it is made.
    CreatedUserApiKey:
      type: object
      description: >-
        A key that has just been made. It is the only answer that ever carries
        the secret, in `key`. Store it now: Widgetic keeps only a hash of it,
        and it cannot be shown again.
      properties:
        key:
          type: string
          description: >-
            The secret. Send it as a bearer token, in the `Authorization`
            header. Shown once
          example: wgt_prt_YOUR_NEW_KEY
        keyId:
          type: string
          description: The id of the key
          example: 5f0c1e2a-7a41-4c5e-9f3b-0c2a8b1d6e44
        keyPreview:
          type: string
          example: wgt_prt_ab12...
        name:
          type: string
          example: Production
        createdAt:
          type: string
          format: date-time
        expiresAt:
          type: string
          format: date-time
          nullable: true
        isActive:
          type: boolean
        scopes:
          type: array
          items:
            $ref: '#/components/schemas/ApiKeyScope'
          description: What the key may do. A key made without scopes holds every scope
        preset:
          type: string
          enum:
            - read_only
            - content
            - full_access
            - custom
          description: The name the scopes amount to
      required:
        - key
        - keyId
        - keyPreview
        - name
        - createdAt
        - isActive
        - scopes
        - preset
      example:
        key: wgt_prt_YOUR_NEW_KEY
        keyId: 5f0c1e2a-7a41-4c5e-9f3b-0c2a8b1d6e44
        keyPreview: wgt_prt_ab12...
        name: Production
        createdAt: '2026-01-15T09:30:00.000Z'
        expiresAt: '2026-01-15T09:30:00.000Z'
        isActive: true
        scopes:
          - Example ApiKeyScope
        preset: Example preset
    ResponseMeta:
      type: object
      description: >-
        Extra information about the answer: where a page sits in a list, the
        cursor of the next page, or warnings about a call that succeeded with a
        caveat.
      additionalProperties: true
      properties:
        page:
          type: integer
          description: The page that was read, counting from 1.
          example: 1
        size:
          type: integer
          description: How many items one page holds.
          example: 20
        total:
          type: integer
          description: How many items the whole list has.
          example: 135
        limit:
          type: integer
          description: The same as `size`.
          example: 20
        offset:
          type: integer
          description: How many items come before this page.
          example: 0
        hasNextPage:
          type: boolean
          example: true
        hasPreviousPage:
          type: boolean
          example: false
        nextCursor:
          type: string
          nullable: true
          description: >-
            Pass this as `cursor` to read the next page of a list that is read
            by cursor. `null` on the last page.
        warnings:
          type: array
          items:
            type: string
          description: >-
            Things the call did not do, though it succeeded (for example a table
            that could not be created just now).
    ErrorDetail:
      type: object
      required:
        - code
        - message
        - timestamp
      properties:
        code:
          type: string
          description: Machine-readable error code.
          example: RESOURCE_NOT_FOUND
        message:
          type: string
          description: Human-readable explanation.
          example: Composition not found
        param:
          type: string
          description: The field that caused the error, when one did.
          example: name
        docUrl:
          type: string
          description: Where this error is explained.
        suggestedAction:
          type: string
          description: What to do about it.
        retryAfter:
          type: integer
          description: >-
            Seconds to wait before trying again (rate limits). The same number
            is in the `Retry-After` header.
          example: 60
        details:
          type: object
          additionalProperties: true
          description: >-
            More about the error: the fields that failed, a limit that was
            reached.
        correlationId:
          type: string
          description: Quote this when you ask support about the call.
          example: req_123e4567-e89b-12d3-a456-426614174000
        timestamp:
          type: string
          format: date-time
          example: '2026-10-15T10:00:00.000Z'
  securitySchemes:
    apiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

````

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