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

> ## Agent Instructions
> Internal links on these pages omit the .md extension. Append .md to a docs page URL, or send an Accept: text/markdown header, to get that page as markdown.

# Get the public key set

> <Info>This API is in beta. Endpoints, fields, and behavior may still change, so avoid depending on it in production.</Info>

Returns the public keys that verify the workspace's event signatures. Needs no authentication. An unknown workspace, or one with no keys yet, gets an empty `keys` list.

Keys come newest first, a page at a time; follow `next_cursor` until it's null. A workspace holds one key, or two during a rotation, so the default page is usually the whole set.

The signature header carries no key ID, so try every key, and fetch the set again once when none verifies. During a key rotation, events carry one signature per key. Responses carry an `ETag`; send it back as `If-None-Match` to get `304 Not Modified` when nothing changed. A non-empty set may be cached for 5 minutes. Rate limit: 600 requests a minute per client IP.



## OpenAPI

````yaml /developers/references/outbound-webhooks/outbound-webhooks-openapi.json get /workspaces/{workspace_id}/keys
openapi: 3.1.0
info:
  title: Base44 Outbound Webhooks API
  description: >

    Register HTTPS endpoints that Base44 notifies when something happens in your
    workspace, such as an

    app being published or the AI builder finishing a message. The events
    section lists every event an

    endpoint can receive.


    **Authentication:** send a workspace API key with the
    `outbound_webhooks:write` scope as

    `Authorization: Bearer <key>`. The key decides the workspace, so no path
    takes a workspace ID.

    The public key set needs no authentication.


    **Activation:** registering an endpoint sends it a signed `webhook.test.v1`
    event in the same call.

    The endpoint becomes `active` only if it answers with a 2xx status within 5
    seconds and returns the

    event's `data.challenge` in its response body, either as `{"challenge":
    "<value>"}` or as the bare

    value. Until then it receives nothing else.


    **Events:** each event is a CloudEvents 1.0 JSON body, signed with Ed25519
    under the Standard Webhooks

    `v1a` scheme. Verify every request against your workspace's public key set,
    and deduplicate on `id`:

    delivery is at least once and unordered.


    **Rate limits**, per workspace and multiplied by your plan's rate-limit
    tier:

    - endpoint reads (list, get): 300 a minute

    - endpoint writes (register, update, pause, resume, delete): 60 a minute

    - activation tests (register, test, resuming an auto-paused endpoint): 10 a
    minute

    - the public key set: 600 a minute per client IP


    A `429` carries `Retry-After`. Errors use one envelope, `{"error": {"code",
    "message", "details"}}`;

    match on `error.code`, never on the message.
  version: 1.0.0
servers:
  - url: https://app.base44.com/api/v1/outbound-webhooks
    description: Outbound Webhooks API
security:
  - WorkspaceApiKey: []
tags:
  - name: outbound-webhooks
    description: >-
      Register and manage the endpoints that receive events, and get the keys
      that verify them.
  - name: events
    description: The events Base44 sends to your endpoints.
paths:
  /workspaces/{workspace_id}/keys:
    get:
      tags:
        - outbound-webhooks
      summary: Get the public key set
      description: >-
        <Info>This API is in beta. Endpoints, fields, and behavior may still
        change, so avoid depending on it in production.</Info>


        Returns the public keys that verify the workspace's event signatures.
        Needs no authentication. An unknown workspace, or one with no keys yet,
        gets an empty `keys` list.


        Keys come newest first, a page at a time; follow `next_cursor` until
        it's null. A workspace holds one key, or two during a rotation, so the
        default page is usually the whole set.


        The signature header carries no key ID, so try every key, and fetch the
        set again once when none verifies. During a key rotation, events carry
        one signature per key. Responses carry an `ETag`; send it back as
        `If-None-Match` to get `304 Not Modified` when nothing changed. A
        non-empty set may be cached for 5 minutes. Rate limit: 600 requests a
        minute per client IP.
      operationId: get_public_key_set_workspaces__workspace_id__keys_get
      parameters:
        - name: workspace_id
          in: path
          required: true
          schema:
            type: string
            description: The workspace's ID.
            examples:
              - 67f3954555578059d9273f7e
            title: Workspace Id
          description: The workspace's ID.
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            maximum: 50
            minimum: 1
            description: How many keys to return.
            examples:
              - 10
            default: 10
            title: Limit
          description: How many keys to return.
        - name: cursor
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: 'null'
            description: >-
              The `next_cursor` from the previous page. Omit it for the first
              page.
            examples:
              - gAAAAABn7vJx...
            title: Cursor
          description: >-
            The `next_cursor` from the previous page. Omit it for the first
            page.
        - name: if-none-match
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: 'null'
            title: If-None-Match
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicKeySetResponse'
          headers:
            X-Request-Id:
              description: Identifies this request. Quote it when you contact support.
              schema:
                type: string
            X-RateLimit-Limit:
              description: Requests allowed in the current window.
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: Requests left in the current window.
              schema:
                type: integer
            X-RateLimit-Reset:
              description: When the current window resets, in Unix seconds.
              schema:
                type: integer
            ETag:
              description: Send it back as `If-None-Match`.
              schema:
                type: string
            Cache-Control:
              description: '`public, max-age=300` for a non-empty set.'
              schema:
                type: string
        '400':
          description: >-
            `invalid_request` — a malformed body, an unusable target URL, an
            unknown event type, or an invalid or expired cursor.
          headers:
            X-Request-Id:
              description: Identifies this request. Quote it when you contact support.
              schema:
                type: string
          content:
            application/json:
              example:
                error:
                  code: invalid_request
                  message: The request is invalid.
                  details:
                    fields:
                      - field: target_url
                        problem: Field required
              schema:
                $ref: '#/components/schemas/ApiErrorResponse'
        '429':
          description: >-
            `rate_limited` — request throttling. Retryable; honour
            `Retry-After`.
          headers:
            X-Request-Id:
              description: Identifies this request. Quote it when you contact support.
              schema:
                type: string
            Retry-After:
              description: Seconds to wait before retrying.
              schema:
                type: integer
            X-RateLimit-Limit:
              description: Requests allowed in the current window.
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: Requests left in the current window.
              schema:
                type: integer
            X-RateLimit-Reset:
              description: When the current window resets, in Unix seconds.
              schema:
                type: integer
          content:
            application/json:
              example:
                error:
                  code: rate_limited
                  message: >-
                    Rate limit exceeded. Retry after the interval in the
                    Retry-After header.
                  details:
                    retry_after_seconds: 42
              schema:
                $ref: '#/components/schemas/ApiErrorResponse'
        '500':
          description: >-
            `internal_error` — an unexpected failure. With `details.reason:
            replacement_mint_failed`, a signing-key revoke took effect but its
            replacement was not created; repeat the revoke to create it.
          headers:
            X-Request-Id:
              description: Identifies this request. Quote it when you contact support.
              schema:
                type: string
          content:
            application/json:
              example:
                error:
                  code: internal_error
                  message: An internal error occurred. Retry with exponential backoff.
                  details: {}
              schema:
                $ref: '#/components/schemas/ApiErrorResponse'
      security: []
components:
  schemas:
    PublicKeySetResponse:
      properties:
        signature_scheme:
          type: string
          title: Signature Scheme
          description: The scheme tag in the `webhook-signature` header. Always `v1a`.
        keys:
          items:
            $ref: '#/components/schemas/PublicSigningKey'
          type: array
          title: Keys
          description: This page of keys, newest first. Empty when the workspace has none.
        next_cursor:
          anyOf:
            - type: string
            - type: 'null'
          title: Next Cursor
          description: >-
            Opaque continuation token. Null on the last page. Pass it back as
            `cursor`; a receiver needs every page to verify during a rotation.
      type: object
      required:
        - signature_scheme
        - keys
      title: PublicKeySetResponse
      description: The keys that currently sign the workspace's events.
      examples:
        - keys:
            - algorithm: ed25519_v1
              kid: whk_1
              public_key: whpk_tuaUMN99+3lmXIpT0k7KusnxtBRgT5EMGAbyjTWLkJY=
              valid_from: '2026-09-27T13:11:15.000Z'
          signature_scheme: v1a
    ApiErrorResponse:
      properties:
        error:
          $ref: '#/components/schemas/ApiErrorBody'
          description: What went wrong.
      type: object
      required:
        - error
      title: ApiErrorResponse
    PublicSigningKey:
      properties:
        kid:
          type: string
          title: Kid
          description: Identifies the key in this set. Requests don't carry it.
        algorithm:
          type: string
          title: Algorithm
          description: Always `ed25519_v1`.
        public_key:
          type: string
          title: Public Key
          description: >-
            The `whpk_`-prefixed Ed25519 public key: standard base64 of the 32
            raw key bytes. Standard Webhooks libraries accept it as is.
        valid_from:
          type: string
          title: Valid From
          description: When the key started signing, in UTC.
        valid_until:
          anyOf:
            - type: string
            - type: 'null'
          title: Valid Until
          description: >-
            When a key being rotated out stops signing, in UTC. Null for the
            current key.
      type: object
      required:
        - kid
        - algorithm
        - public_key
        - valid_from
      title: PublicSigningKey
    ApiErrorBody:
      properties:
        code:
          type: string
          title: Code
          description: Stable machine-readable cause. Match on this, not the message.
        message:
          type: string
          title: Message
          description: Human-readable prose. Not a stable contract.
        details:
          additionalProperties: true
          type: object
          title: Details
          description: Documented structured context, or empty.
      type: object
      required:
        - code
        - message
      title: ApiErrorBody
  securitySchemes:
    WorkspaceApiKey:
      type: http
      scheme: bearer
      description: >-
        A workspace API key with the `outbound_webhooks:write` scope, sent as
        `Authorization: Bearer <key>`.

````

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