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

# Keep the data of a partner's compositions in a Neon database of its own

> Points the partner at a Neon database of its own. From then on, what visitors leave in the compositions of
the partner's managed users, and of the compositions made in the partner's own account (the votes, ratings,
answers and form submissions of the widgets that collect them), is stored in that database and nowhere else.
Widgetic's own database is not used for them any more, and nothing of it is copied there.

**What to give.** The connection string of a Neon database and a role made for this, as Neon shows it under
*Connect* (`postgresql://<role>:<password>@ep-<name>.<region>.aws.neon.tech/<database>`). Only Neon databases
are accepted; a string for any other host is refused with 400. What the database needs:
- an empty database of its own, so that nothing else of yours shares it. Widgetic makes one schema in it for
  each composition (named `comp_` followed by the composition's id). In that schema go the tables the widget
  declares for what its visitors leave, and two small tables of Widgetic's own, whose names start with
  `_widgetic_`: the context a host page sent with an entry, and how far Widgetic has read the entries to tell
  you about new ones. It touches nothing else;
- a role that may create schemas and tables in it (the CREATE privilege on the database). The role Neon makes
  with a project has it. A role made in the Neon console can also read and write every other database of the
  same project, so if you keep other data in Neon give Widgetic a project of its own, or make the role with
  SQL (`CREATE ROLE ... LOGIN PASSWORD ...`) and grant it only the CREATE privilege on this database;
- connections from anywhere: Widgetic's addresses are not fixed, so a Neon project that only accepts
  connections from an allow list of addresses cannot be used. A suspended compute is fine: it wakes on the
  first request.

**It is checked before it is saved.** Widgetic makes a schema in your database, creates a table in it, writes
a row, reads it back, and removes the schema again, all in one step, so that nothing is left if anything
fails. If it cannot be done nothing is saved and the answer is 422 `PARTNER_DATABASE_UNVERIFIED`:
`details.reason` says why (`INVALID_CREDENTIALS`, `DATABASE_NOT_FOUND`, `ACCESS_DENIED`, `ACCESS_BLOCKED`,
`ENDPOINT_NOT_FOUND`, `ENDPOINT_UNREACHABLE`, `TIMEOUT`, `READ_ONLY`, `RATE_LIMITED`, `DATABASE_UNAVAILABLE`,
`PROBE_MISMATCH` or `DATABASE_ERROR`) and `message` says what to do.

**Existing data does not move.** The data of a composition lives in one database. If any composition of the
partner already keeps data somewhere else (in Widgetic's database, or in the database you had set), and the
new database does not hold it, the change is refused with 409 `PARTNER_DATABASE_HOLDS_DATA` and nothing is
changed: those compositions would read as empty from then on. `details.compositionsWithData` says how many.
Set your database before your customers collect data, or delete the compositions you no longer need. Saving
the same database again (a new password) moves nothing and is never refused.

**It is never a fallback.** Once a database is set, a request that needs it while it cannot be used (the
password was changed, the project was deleted) is refused with 503 `PARTNER_DATABASE_UNAVAILABLE`. What the
visitor sent is not stored with Widgetic instead. Fix the connection and set the database again, or check it
with `verifyPartnerDataDatabase`.

**When it takes effect.** A change reaches every part of Widgetic within about a minute.

**What Widgetic keeps.** The connection string is stored encrypted and is never returned or logged; a response
shows the host, the database and the role, never the password. Widgetic's servers open it to read and write the
data of your compositions while they handle a request for it. To cut that off, change the role's password in
Neon: the compositions stop saving until you set the new string.

This needs an API key with full access. The caller must be the person who administers the partner, or a
Widgetic administrator. A retry is harmless: saving the same database twice only refreshes when it was
checked and saved.




## OpenAPI

````yaml /openapi/widgetic-api-public.json put /partners/{partnerId}/data-database
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:
  /partners/{partnerId}/data-database:
    put:
      tags:
        - Partners
      summary: Keep the data of a partner's compositions in a Neon database of its own
      description: >
        Points the partner at a Neon database of its own. From then on, what
        visitors leave in the compositions of

        the partner's managed users, and of the compositions made in the
        partner's own account (the votes, ratings,

        answers and form submissions of the widgets that collect them), is
        stored in that database and nowhere else.

        Widgetic's own database is not used for them any more, and nothing of it
        is copied there.


        **What to give.** The connection string of a Neon database and a role
        made for this, as Neon shows it under

        *Connect*
        (`postgresql://<role>:<password>@ep-<name>.<region>.aws.neon.tech/<database>`).
        Only Neon databases

        are accepted; a string for any other host is refused with 400. What the
        database needs:

        - an empty database of its own, so that nothing else of yours shares it.
        Widgetic makes one schema in it for
          each composition (named `comp_` followed by the composition's id). In that schema go the tables the widget
          declares for what its visitors leave, and two small tables of Widgetic's own, whose names start with
          `_widgetic_`: the context a host page sent with an entry, and how far Widgetic has read the entries to tell
          you about new ones. It touches nothing else;
        - a role that may create schemas and tables in it (the CREATE privilege
        on the database). The role Neon makes
          with a project has it. A role made in the Neon console can also read and write every other database of the
          same project, so if you keep other data in Neon give Widgetic a project of its own, or make the role with
          SQL (`CREATE ROLE ... LOGIN PASSWORD ...`) and grant it only the CREATE privilege on this database;
        - connections from anywhere: Widgetic's addresses are not fixed, so a
        Neon project that only accepts
          connections from an allow list of addresses cannot be used. A suspended compute is fine: it wakes on the
          first request.

        **It is checked before it is saved.** Widgetic makes a schema in your
        database, creates a table in it, writes

        a row, reads it back, and removes the schema again, all in one step, so
        that nothing is left if anything

        fails. If it cannot be done nothing is saved and the answer is 422
        `PARTNER_DATABASE_UNVERIFIED`:

        `details.reason` says why (`INVALID_CREDENTIALS`, `DATABASE_NOT_FOUND`,
        `ACCESS_DENIED`, `ACCESS_BLOCKED`,

        `ENDPOINT_NOT_FOUND`, `ENDPOINT_UNREACHABLE`, `TIMEOUT`, `READ_ONLY`,
        `RATE_LIMITED`, `DATABASE_UNAVAILABLE`,

        `PROBE_MISMATCH` or `DATABASE_ERROR`) and `message` says what to do.


        **Existing data does not move.** The data of a composition lives in one
        database. If any composition of the

        partner already keeps data somewhere else (in Widgetic's database, or in
        the database you had set), and the

        new database does not hold it, the change is refused with 409
        `PARTNER_DATABASE_HOLDS_DATA` and nothing is

        changed: those compositions would read as empty from then on.
        `details.compositionsWithData` says how many.

        Set your database before your customers collect data, or delete the
        compositions you no longer need. Saving

        the same database again (a new password) moves nothing and is never
        refused.


        **It is never a fallback.** Once a database is set, a request that needs
        it while it cannot be used (the

        password was changed, the project was deleted) is refused with 503
        `PARTNER_DATABASE_UNAVAILABLE`. What the

        visitor sent is not stored with Widgetic instead. Fix the connection and
        set the database again, or check it

        with `verifyPartnerDataDatabase`.


        **When it takes effect.** A change reaches every part of Widgetic within
        about a minute.


        **What Widgetic keeps.** The connection string is stored encrypted and
        is never returned or logged; a response

        shows the host, the database and the role, never the password.
        Widgetic's servers open it to read and write the

        data of your compositions while they handle a request for it. To cut
        that off, change the role's password in

        Neon: the compositions stop saving until you set the new string.


        This needs an API key with full access. The caller must be the person
        who administers the partner, or a

        Widgetic administrator. A retry is harmless: saving the same database
        twice only refreshes when it was

        checked and saved.
      operationId: setPartnerDataDatabase
      parameters:
        - in: path
          name: partnerId
          required: true
          schema:
            type: string
            format: uuid
          description: The partner whose database to set
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetPartnerDataDatabaseRequest'
      responses:
        '200':
          description: The database passed the check and was saved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartnerDataDatabaseResponse'
        '400':
          description: >-
            The connection string is missing, malformed, or not a Neon one. The
            error's param names it.
          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 caller does not administer this partner, or the API key has not
            full access
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: There is no such partner (reported to administrators only)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: >-
            Compositions of the partner already keep data elsewhere
            (PARTNER_DATABASE_HOLDS_DATA), or the partner has no administrator
            account (BUSINESS_STATE_CONFLICT). Nothing was changed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: >-
            The database did not pass the check (PARTNER_DATABASE_UNVERIFIED).
            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.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: >-
            A partner's own database is not available on this server
            (PARTNER_DATABASE_UNAVAILABLE), or Widgetic could not check what the
            current database holds. Nothing was changed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - apiKeyAuth: []
      x-code-samples:
        - lang: TypeScript
          label: TypeScript SDK
          source: >
            import { Configuration, PartnersApi } from '@widgetic/api-sdk-ts';


            const partnersApi = new PartnersApi(new Configuration({ apiKey:
            'YOUR_API_KEY' }));


            const { data: database } = await
            partnersApi.setPartnerDataDatabase({
              partnerId,
              setPartnerDataDatabaseRequest: {
                connectionString: 'postgresql://widgetic_data:YOUR_PASSWORD@ep-cool-name-123456.eu-central-1.aws.neon.tech/neondb',
              },
            });

            console.log(database.host, database.databaseName,
            database.verifiedAt);
components:
  schemas:
    SetPartnerDataDatabaseRequest:
      type: object
      required:
        - connectionString
      properties:
        connectionString:
          type: string
          format: password
          writeOnly: true
          description: >-
            The connection string of a Neon database and role you made for this,
            as Neon shows it under Connect:
            `postgresql://<role>:<password>@ep-<name>.<region>.aws.neon.tech/<database>`.
            Either the direct or the pooled host works. Everything after the
            database name (`?sslmode=...`) is ignored. It is stored encrypted
            and is never returned or logged.
          example: >-
            postgresql://widgetic_data:YOUR_PASSWORD@ep-cool-name-123456.eu-central-1.aws.neon.tech/neondb
      example:
        connectionString: >-
          postgresql://widgetic_data:YOUR_PASSWORD@ep-cool-name-123456.eu-central-1.aws.neon.tech/neondb
    PartnerDataDatabaseResponse:
      type: object
      required:
        - success
        - data
      properties:
        success:
          type: boolean
          description: Always `true` on a success.
          example: true
        data:
          $ref: '#/components/schemas/PartnerDataDatabase'
        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'
    PartnerDataDatabase:
      type: object
      description: >-
        The Neon database your compositions keep what visitors leave in them
        (votes, ratings, answers, form submissions). The connection string and
        its password are never part of this: they are given once, when the
        database is set.
      required:
        - partnerId
        - host
        - databaseName
        - roleName
        - verifiedAt
        - lastVerificationError
        - lastVerificationFailedAt
        - updatedAt
      properties:
        partnerId:
          type: string
          format: uuid
        host:
          type: string
          description: >-
            The Neon host of the database, as it appears in its connection
            string (`ep-....neon.tech`).
          example: ep-cool-name-123456.eu-central-1.aws.neon.tech
        databaseName:
          type: string
          description: The name of the database in your Neon project.
          example: neondb
        roleName:
          type: string
          description: The role Widgetic connects as.
          example: widgetic_data
        verifiedAt:
          type: string
          format: date-time
          description: When the database last passed the check.
        lastVerificationError:
          type: string
          nullable: true
          description: >-
            Why the latest re-check failed, in a sentence you can act on. Null
            when the last check passed.
        lastVerificationFailedAt:
          type: string
          format: date-time
          nullable: true
          description: When the latest re-check failed. Null when the last check passed.
        updatedAt:
          type: string
          format: date-time
      example:
        partnerId: 3fa85f64-5717-4562-b3fc-2c963f66afa6
        host: ep-cool-name-123456.eu-central-1.aws.neon.tech
        databaseName: neondb
        roleName: widgetic_data
        verifiedAt: '2026-01-15T09:30:00.000Z'
        lastVerificationError: Example lastVerificationError
        lastVerificationFailedAt: '2026-01-15T09:30:00.000Z'
        updatedAt: '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).
    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.