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

# Connect an adapter key to an account

> Gives an adapter the key it needs to act through a service you have an account with, for the widgets you made.
Take the `adapterId` and `credentialKey` from `getUserAdapterConnections`. Saving a key that is already
connected replaces it.

**What Widgetic does with it.** The key is stored encrypted, bound to this account, this adapter and this
credential, so that a stored value cannot be moved to another. It is never returned, never logged and never
shown to a visitor of a widget. A server of Widgetic opens it for the one call a widget makes through the
adapter, uses it to call the provider on your behalf, and discards it. To cut that off, remove the key here, or
revoke it with the provider.

**What is checked.** That the adapter exists and takes this credential, that an owner may keep it (some are
for a partner only) and that the text is one line of at most 2048 characters. Whether the provider accepts
the key is not checked here: it shows the first time a widget uses it, and the widget tells its owner.

**An address that has to be confirmed.** Some credentials are not a key but the email address Widgetic sends a
widget's messages to (`confirmation: email_code` in `getUserAdapterConnections`). Saving one checks that it is one
email address and emails a six-digit code to it; the answer says `awaitingConfirmation: true`, and nothing is sent
to the address until the code is given back with `confirmUserAdapterConnection`. Saving the same address again
before it is confirmed emails a new code (not sooner than a minute after the last); saving the address that is
already confirmed changes nothing. If the code cannot be emailed, nothing is saved.

A widget whose adapter has no key connected, or whose address is not confirmed yet, is not charged and does not
run that adapter: its visitors are told the feature is unavailable, and the owner is told once a day what to do.

This needs an API key with full access. The caller must be the account itself or a Widgetic administrator. A
retry is harmless: saving the same key twice only refreshes when it was set.




## OpenAPI

````yaml /openapi/widgetic-api-public.json put /users/{userId}/adapter-connections/{adapterId}/{credentialKey}
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/{userId}/adapter-connections/{adapterId}/{credentialKey}:
    put:
      tags:
        - Users
      summary: Connect an adapter key to an account
      description: >
        Gives an adapter the key it needs to act through a service you have an
        account with, for the widgets you made.

        Take the `adapterId` and `credentialKey` from
        `getUserAdapterConnections`. Saving a key that is already

        connected replaces it.


        **What Widgetic does with it.** The key is stored encrypted, bound to
        this account, this adapter and this

        credential, so that a stored value cannot be moved to another. It is
        never returned, never logged and never

        shown to a visitor of a widget. A server of Widgetic opens it for the
        one call a widget makes through the

        adapter, uses it to call the provider on your behalf, and discards it.
        To cut that off, remove the key here, or

        revoke it with the provider.


        **What is checked.** That the adapter exists and takes this credential,
        that an owner may keep it (some are

        for a partner only) and that the text is one line of at most 2048
        characters. Whether the provider accepts

        the key is not checked here: it shows the first time a widget uses it,
        and the widget tells its owner.


        **An address that has to be confirmed.** Some credentials are not a key
        but the email address Widgetic sends a

        widget's messages to (`confirmation: email_code` in
        `getUserAdapterConnections`). Saving one checks that it is one

        email address and emails a six-digit code to it; the answer says
        `awaitingConfirmation: true`, and nothing is sent

        to the address until the code is given back with
        `confirmUserAdapterConnection`. Saving the same address again

        before it is confirmed emails a new code (not sooner than a minute after
        the last); saving the address that is

        already confirmed changes nothing. If the code cannot be emailed,
        nothing is saved.


        A widget whose adapter has no key connected, or whose address is not
        confirmed yet, is not charged and does not

        run that adapter: its visitors are told the feature is unavailable, and
        the owner is told once a day what to do.


        This needs an API key with full access. The caller must be the account
        itself or a Widgetic administrator. A

        retry is harmless: saving the same key twice only refreshes when it was
        set.
      operationId: setUserAdapterConnection
      parameters:
        - in: path
          name: userId
          required: true
          schema:
            type: string
            format: uuid
          description: The account that keeps the key. For your own, your own user id
        - in: path
          name: adapterId
          required: true
          schema:
            type: string
            maxLength: 40
          description: The adapter the key is for
        - in: path
          name: credentialKey
          required: true
          schema:
            type: string
            maxLength: 40
          description: The key's name within the adapter
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetAdapterConnectionRequest'
      responses:
        '200':
          description: The key was saved
          content:
            application/json:
              schema:
                title: AdapterConnectionResponse
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    $ref: '#/components/schemas/AdapterConnection'
                  meta:
                    $ref: '#/components/schemas/ResponseMeta'
                required:
                  - success
                  - data
        '400':
          description: >-
            A path value or the body is malformed. The body takes exactly one
            field, `credential`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: The API key is missing or invalid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: >-
            The key belongs to another account, or the API key has not full
            access
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: >-
            There is no such adapter, the adapter has no such credential, an
            owner may not keep it, or the account does not exist
            (ADAPTER_NOT_FOUND, CREDENTIAL_NOT_FOUND,
            CREDENTIAL_NOT_FOR_THIS_ACCOUNT, HOLDER_NOT_FOUND)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: >-
            The credential is not one line of text of at most 2048 characters
            (CREDENTIAL_VALUE_INVALID), is not one email address where one is
            needed (CREDENTIAL_ADDRESS_INVALID), or the email provider refused
            the address a code was to be emailed to
            (CONFIRMATION_ADDRESS_REFUSED). Nothing was saved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: >-
            Too many changes in a short time (try again after the Retry-After
            seconds), or too many codes were emailed to that address or for that
            account (CONFIRMATION_TOO_SOON, CONFIRMATION_LIMIT_REACHED)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: >-
            Adapter keys cannot be saved on this server right now
            (ADAPTER_CONNECTIONS_UNAVAILABLE), or the code could not be emailed
            (CONFIRMATION_NOT_SENT). Nothing was changed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - apiKeyAuth: []
      x-code-samples:
        - lang: TypeScript
          label: TypeScript SDK
          source: >
            import { Configuration, UsersApi } from '@widgetic/api-sdk-ts';


            const usersApi = new UsersApi(new Configuration({ apiKey:
            'YOUR_API_KEY' }));


            const { data: connection } = await
            usersApi.setUserAdapterConnection({
              userId,
              adapterId: 'email',
              credentialKey: 'resendApiKey',
              setAdapterConnectionRequest: { credential: 'YOUR_PROVIDER_KEY' },
            });

            console.log(connection.connected, connection.updatedAt);


            // An address a widget's messages are emailed to: a code is emailed
            to it, and it is used once the code is given back.

            const { data: address } = await usersApi.setUserAdapterConnection({
              userId,
              adapterId: 'email',
              credentialKey: 'recipient',
              setAdapterConnectionRequest: { credential: 'owner@example.com' },
            });

            console.log(address.awaitingConfirmation);
components:
  schemas:
    SetAdapterConnectionRequest:
      type: object
      required:
        - credential
      additionalProperties: false
      properties:
        credential:
          type: string
          format: password
          writeOnly: true
          maxLength: 2048
          description: >-
            The key exactly as its provider gave it: one line of text, at most
            2048 characters. It is stored encrypted and is never returned or
            logged. For a credential that is an email address (`confirmation:
            email_code`), write that one address, with nothing else
          example: YOUR_PROVIDER_KEY
      example:
        credential: YOUR_PROVIDER_KEY
    AdapterConnection:
      type: object
      description: >-
        A key an adapter can take from an account, and whether the account has
        connected it. The key itself is never part of this: it is given once,
        when it is connected, and Widgetic keeps it encrypted.
      required:
        - adapterId
        - adapterLabel
        - credentialKey
        - label
        - scope
        - connected
        - updatedAt
        - confirmation
        - awaitingConfirmation
        - confirmedAt
      properties:
        adapterId:
          type: string
          description: The adapter the key is for (its id in the adapter catalog)
          example: email
        adapterLabel:
          type: string
          description: What the adapter is called to its owner
          example: Email
        credentialKey:
          type: string
          description: >-
            The name of the key within the adapter. Use it, with the adapter id,
            to connect or remove the key
          example: resendApiKey
        label:
          type: string
          description: What the key is called to its owner
          example: Resend API key
        scope:
          type: string
          enum:
            - owner
            - partner
            - owner_or_partner
          description: >-
            Who may keep this key. `owner` is the person who made the widget, in
            their own account. `partner` is a partner, for the widgets of the
            customers it manages. `owner_or_partner` is either; for a widget of
            a managed customer the partner's key is used.
        connected:
          type: boolean
          description: Whether this account has connected the key
        updatedAt:
          type: string
          format: date-time
          nullable: true
          description: When the key was last set. Null when it is not connected
        confirmation:
          type: string
          nullable: true
          description: >-
            How this credential is proved before it is used. `email_code` means
            it is an email address: saving it emails a code to it, and it is
            used only after the code is given back
            (`confirmUserAdapterConnection`, `confirmPartnerAdapterConnection`).
            Null for a credential that is used as soon as it is saved
          example: email_code
        awaitingConfirmation:
          type: boolean
          description: >-
            True when the credential is connected but has not been confirmed
            yet, so no adapter uses it. Always false when `confirmation` is null
            or the credential is not connected
        confirmedAt:
          type: string
          format: date-time
          nullable: true
          description: >-
            When the credential was confirmed. Null when it is not connected, is
            awaiting its code, or needs no confirmation
      example:
        adapterId: email
        adapterLabel: Email
        credentialKey: resendApiKey
        label: Resend API key
        scope: Example scope
        connected: true
        updatedAt: '2026-01-15T09:30:00.000Z'
        confirmation: email_code
        awaitingConfirmation: true
        confirmedAt: '2026-01-15T09:30:00.000Z'
    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).
    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'
    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.