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

# Set where a partner's uploaded files are stored

> Points the partner at a bucket of its own on an S3-compatible service (Cloudflare R2, Amazon S3,
Wasabi, MinIO and the like). From then on, every file the account that administers the
partner stores is kept in that bucket instead of with Widgetic, whichever way it arrives: a file sent with
`uploadFile` (with an API key, an SDK token, an editor session or from the Widgetic website), a resumable
upload, the media library, and the files the agent tools and the MCP server save. It takes none of the
account's storage allowance, and its `url` is served from `publicBaseUrl`. The chunks of a resumable
upload wait in Widgetic's temporary staging area until it is completed and are removed then: the finished
file is stored in your bucket and nowhere else.

A destination is required. Partner storage is not optional: your customers' files, and your own however they
arrive, are kept in this destination and nowhere else. An editor session
(`createManagedUserEditorSession`) of any surface cannot be made until a destination is set (409
`PARTNER_STORAGE_REQUIRED`), a destination removed later stops the uploads of the sessions already made
(the same 409), and your own upload with no destination (on any route, including a resumable upload, which
is refused when it is started) is refused in the same way instead of being stored with Widgetic. A partner
cannot be activated until it has a destination that works.

The destination is checked before it is saved. Widgetic writes a small test file under
`<keyPrefix>widgetic-check/`, reads it back from `<publicBaseUrl>/<its key>`, and deletes it. If any
step fails nothing is saved and the answer is 422 `STORAGE_DESTINATION_UNVERIFIED`: `details.step` says
where (`endpoint`, `write` or `public_read`), `details.reason` says why, and `param` names the field most
likely to be wrong.

What the bucket needs:
- an access key that may put objects into it (it may also delete objects, which lets the test file be
  cleaned up, but that is not required);
- its files publicly readable at `publicBaseUrl`, either because the bucket is public or through a CDN.

The secret access key is stored encrypted and is never returned or logged; a response shows only the last
four characters of the access key id (`accessKeyHint`). Saving replaces the whole destination, so send
every field again, including the secret. Files already stored stay where they are, and keep working for
as long as that bucket stays publicly readable.

Once a destination is set, an upload is never stored with Widgetic instead: when the destination cannot be
used the upload is refused with 503 `STORAGE_DESTINATION_UNAVAILABLE`, and when the bucket rejects the
file with 502 `STORAGE_DESTINATION_WRITE_FAILED`.

The caller must be the person who administers the partner (an API key of that account works); anyone else
is refused with 403 whether or not the partner exists. Widgetic administrators can set any partner's.




## OpenAPI

````yaml /openapi/widgetic-api-public.json put /partners/{partnerId}/storage
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}/storage:
    put:
      tags:
        - Partners
      summary: Set where a partner's uploaded files are stored
      description: >
        Points the partner at a bucket of its own on an S3-compatible service
        (Cloudflare R2, Amazon S3,

        Wasabi, MinIO and the like). From then on, every file the account that
        administers the

        partner stores is kept in that bucket instead of with Widgetic,
        whichever way it arrives: a file sent with

        `uploadFile` (with an API key, an SDK token, an editor session or from
        the Widgetic website), a resumable

        upload, the media library, and the files the agent tools and the MCP
        server save. It takes none of the

        account's storage allowance, and its `url` is served from
        `publicBaseUrl`. The chunks of a resumable

        upload wait in Widgetic's temporary staging area until it is completed
        and are removed then: the finished

        file is stored in your bucket and nowhere else.


        A destination is required. Partner storage is not optional: your
        customers' files, and your own however they

        arrive, are kept in this destination and nowhere else. An editor session

        (`createManagedUserEditorSession`) of any surface cannot be made until a
        destination is set (409

        `PARTNER_STORAGE_REQUIRED`), a destination removed later stops the
        uploads of the sessions already made

        (the same 409), and your own upload with no destination (on any route,
        including a resumable upload, which

        is refused when it is started) is refused in the same way instead of
        being stored with Widgetic. A partner

        cannot be activated until it has a destination that works.


        The destination is checked before it is saved. Widgetic writes a small
        test file under

        `<keyPrefix>widgetic-check/`, reads it back from `<publicBaseUrl>/<its
        key>`, and deletes it. If any

        step fails nothing is saved and the answer is 422
        `STORAGE_DESTINATION_UNVERIFIED`: `details.step` says

        where (`endpoint`, `write` or `public_read`), `details.reason` says why,
        and `param` names the field most

        likely to be wrong.


        What the bucket needs:

        - an access key that may put objects into it (it may also delete
        objects, which lets the test file be
          cleaned up, but that is not required);
        - its files publicly readable at `publicBaseUrl`, either because the
        bucket is public or through a CDN.


        The secret access key is stored encrypted and is never returned or
        logged; a response shows only the last

        four characters of the access key id (`accessKeyHint`). Saving replaces
        the whole destination, so send

        every field again, including the secret. Files already stored stay where
        they are, and keep working for

        as long as that bucket stays publicly readable.


        Once a destination is set, an upload is never stored with Widgetic
        instead: when the destination cannot be

        used the upload is refused with 503 `STORAGE_DESTINATION_UNAVAILABLE`,
        and when the bucket rejects the

        file with 502 `STORAGE_DESTINATION_WRITE_FAILED`.


        The caller must be the person who administers the partner (an API key of
        that account works); anyone else

        is refused with 403 whether or not the partner exists. Widgetic
        administrators can set any partner's.
      operationId: setPartnerStorageDestination
      parameters:
        - in: path
          name: partnerId
          required: true
          schema:
            type: string
            format: uuid
          description: The partner whose storage destination to set
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetPartnerStorageDestinationRequest'
      responses:
        '200':
          description: The destination passed the check and was saved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartnerStorageDestinationResponse'
        '400':
          description: >-
            A field is missing, malformed or not allowed. 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
          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'
        '422':
          description: >-
            The destination did not pass the check
            (STORAGE_DESTINATION_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: >-
            Storage destinations are not enabled on this server
            (PARTNER_STORAGE_DISABLED)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - apiKeyAuth: []
components:
  schemas:
    SetPartnerStorageDestinationRequest:
      type: object
      description: >
        The bucket to keep the partner's files in, and the key that may write to
        it. The field names are camelCase, like every answer of the API; the
        snake_case spelling (`keyPrefix`) is read too.
      additionalProperties: false
      required:
        - endpoint
        - region
        - bucket
        - publicBaseUrl
        - accessKeyId
        - secretAccessKey
      properties:
        endpoint:
          type: string
          description: >
            The HTTPS address of the S3-compatible service, scheme and host only
            (no path, port or credentials). It must be a host name, not an IP
            address, and must resolve to the public internet.
          example: https://abc123.r2.cloudflarestorage.com
        region:
          type: string
          description: >-
            The region the service signs requests for. Use auto where the
            service says so (such as Cloudflare R2).
          example: auto
        bucket:
          type: string
          example: acme-widget-media
        keyPrefix:
          type: string
          default: ''
          description: >
            A folder inside the bucket to write every file under, for example
            widgetic/uploads. Folders are made of letters, digits, dots, hyphens
            and underscores. Leave it out to write at the root of the bucket.
          example: widgetic/
        publicBaseUrl:
          type: string
          description: >
            Where the stored files are served from: the bucket's public address,
            or a CDN in front of it. The address of a file is this value, a
            slash and the file's key in the bucket, which starts with the key
            prefix. A path is allowed
            (https://media.example.com/files/acme-widget-media).
          example: https://media.example.com
        forcePathStyle:
          type: boolean
          default: true
          description: >
            Put the bucket name in the path of a request. Every S3-compatible
            service supports this; set it to false only for a service that needs
            the bucket name in the host name.
        accessKeyId:
          type: string
          description: The id of an access key that may put objects into the bucket
        secretAccessKey:
          type: string
          format: password
          writeOnly: true
          description: >-
            The secret of that access key. It is stored encrypted and is never
            returned.
      example:
        endpoint: https://abc123.r2.cloudflarestorage.com
        region: auto
        bucket: acme-widget-media
        keyPrefix: widgetic/
        publicBaseUrl: https://media.example.com
        forcePathStyle: true
        accessKeyId: Example accessKeyId
        secretAccessKey: Example secretAccessKey
    PartnerStorageDestinationResponse:
      type: object
      required:
        - success
        - data
      properties:
        success:
          type: boolean
          description: Always `true` on a success.
          example: true
        data:
          $ref: '#/components/schemas/PartnerStorageDestination'
        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'
    PartnerStorageDestination:
      type: object
      description: >
        The bucket a partner keeps the files its account uploads in: a bucket on
        an S3-compatible service that the partner owns. The secret access key is
        never returned.
      required:
        - partnerId
        - endpoint
        - region
        - bucket
        - keyPrefix
        - publicBaseUrl
        - forcePathStyle
        - accessKeyHint
        - verifiedAt
        - updatedAt
      properties:
        partnerId:
          type: string
          format: uuid
        endpoint:
          type: string
          description: The HTTPS address of the S3-compatible service, scheme and host only
          example: https://abc123.r2.cloudflarestorage.com
        region:
          type: string
          example: auto
        bucket:
          type: string
          example: acme-widget-media
        keyPrefix:
          type: string
          description: >-
            The folder inside the bucket every file is written under. Empty, or
            folders each followed by a slash.
          example: widgetic/
        publicBaseUrl:
          type: string
          description: >-
            Where the stored files are served from. A file's url is this
            address, a slash and the file's key.
          example: https://media.example.com
        forcePathStyle:
          type: boolean
          description: >-
            Whether the bucket name goes in the path of a request (true) or in
            the host name (false)
        accessKeyHint:
          type: string
          description: >-
            The last four characters of the access key id, to tell which key is
            set. Not enough to use it.
          example: 4F2A
        verifiedAt:
          type: string
          format: date-time
          description: When the destination last passed the check
        lastVerificationError:
          type: string
          nullable: true
          description: >-
            Why the most recent re-check failed. Null while the last check
            passed.
        lastVerificationFailedAt:
          type: string
          format: date-time
          nullable: true
        updatedAt:
          type: string
          format: date-time
      example:
        partnerId: 3fa85f64-5717-4562-b3fc-2c963f66afa6
        endpoint: https://abc123.r2.cloudflarestorage.com
        region: auto
        bucket: acme-widget-media
        keyPrefix: widgetic/
        publicBaseUrl: https://media.example.com
        forcePathStyle: true
        accessKeyHint: 4F2A
        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.