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

# Create a widget session

> Mint a visitor's session server-to-server. You get the bearer token once, here, and never again.

The mint. Your backend authenticates a visitor, asserts who they are, and trades that assertion for a short-lived bearer token the browser uses against the [widget](/docs/api-reference/widgetservice/create-a-new-widget) host.

<Warning>
  Session creation is server-to-server only. This endpoint takes your API key, and your API key must never reach a browser. The browser gets exactly one thing out of this flow: the token in `spec.token`.
</Warning>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const session = await client.widgetSessions.create({
    workspaceId,
    spec: {
      widgetId: 'external_id:acme-support',
      tenant: { id: 'acme-corp', name: 'Acme Corp' },
      subject: { id: 'customer-user-42', name: 'Jane Doe' },
    },
    secrets: [{ name: 'ACME_API_TOKEN', value: visitorToken }],
  });

  // The only time the token exists outside Cadenya:
  res.json({ token: session.spec.token, host: session.info?.host });
  ```

  ```go Go theme={null}
  session, err := client.WidgetSessions.New(ctx, cadenya.WidgetSessionNewParams{
  	WorkspaceID: cadenya.String(workspaceID),
  	Spec: cadenya.WidgetSessionSpecParam{
  		WidgetID: "external_id:acme-support",
  		Tenant:   cadenya.TenantAssertionParam{ID: "acme-corp", Name: cadenya.String("Acme Corp")},
  		Subject:  cadenya.SubjectAssertionParam{ID: "customer-user-42", Name: cadenya.String("Jane Doe")},
  	},
  	Secrets: []cadenya.WidgetSessionNewParamsSecret{
  		{Name: cadenya.String("ACME_API_TOKEN"), Value: cadenya.String(visitorToken)},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  // The only time the token exists outside Cadenya:
  respond(session.Spec.Token, session.Info.Host)
  ```

  ```ruby Ruby theme={null}
  session = cadenya.widget_sessions.create(
    workspace_id: workspace_id,
    spec: {
      widgetId: "external_id:acme-support",
      tenant: {id: "acme-corp", name: "Acme Corp"},
      subject: {id: "customer-user-42", name: "Jane Doe"}
    },
    secrets: [{name: "ACME_API_TOKEN", value: visitor_token}]
  )

  # The only time the token exists outside Cadenya:
  render json: {token: session.spec.token, host: session.info.host}
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.cadenya.com/v1/workspaces/${WORKSPACE_ID}/widget_sessions" \
    -H "Authorization: Bearer ${CADENYA_API_KEY}" \
    -H "Content-Type: application/json" \
    -d '{
          "spec": {
            "widgetId": "external_id:acme-support",
            "tenant": { "id": "acme-corp", "name": "Acme Corp" },
            "subject": { "id": "customer-user-42", "name": "Jane Doe" }
          },
          "secrets": [{ "name": "ACME_API_TOKEN", "value": "'"${VISITOR_TOKEN}"'" }]
        }'
  ```
</CodeGroup>

## The token appears once

`spec.token` is returned by this call and by nothing else. [Reads](/docs/api-reference/widgetsessionservice/get-a-widget-session-by-id) omit it. Hand it to the browser and forget it: the token is short-lived, and the widget refreshes it at the widget host without involving your backend. `spec.tokenExpiresAt` bounds the token; `spec.expiresAt` bounds the session itself, after which it moves to `STATE_EXPIRED` and refreshes stop.

Return `info.host` alongside the token. It is the authoritative hostname the token works against, and clients must not construct it themselves.

## Tenant and subject are assertions

You do not create tenants; you assert them. `spec.tenant.id` is your identifier for the visitor's org (`acme-corp`), and the assertion upserts the [tenant record](/docs/api-reference/tenantservice/list-tenants) in the workspace. `spec.subject` names the person within the tenant, in your namespace. A subject without a tenant is rejected with `InvalidArgument`.

The session carries the assertion to every conversation it creates, which is what scopes conversation listing at the widget host to the visitor's tenant.

## Secrets make the agent act as the visitor

Attach per-visitor credentials (say, a token your backend minted against your own API) and every conversation the session creates carries them. Values are encrypted at rest, interpolated into tool-call headers server-side, and never returned by any API. On a name clash, session secrets beat workspace and tool set secrets, so `ACME_API_TOKEN` means this visitor's token, not the shared one.

## `pinnedParameters` locks tool arguments

A pinned parameter is removed from the JSON schema the model sees, and its value is overwritten server-side on every call. Pin the values a visitor must not influence:

```typescript theme={null}
spec: {
  widgetId: 'external_id:acme-support',
  pinnedParameters: { accountId: 'acme-corp' },
}
```

Now no prompt injection can convince the agent to call a tool against someone else's `accountId`. The model never sees the parameter at all.

## Related

<CardGroup cols={2}>
  <Card title="Revoke a widget session" icon="ban" href="/docs/api-reference/widgetsessionservice/revoke-a-widget-session">
    Kill the token before it expires.
  </Card>

  <Card title="List widget sessions" icon="list" href="/docs/api-reference/widgetsessionservice/list-widget-sessions">
    Filter by widget, tenant, subject, or state.
  </Card>

  <Card title="Create a widget" icon="plus" href="/docs/api-reference/widgetservice/create-a-new-widget">
    The widget this session is minted against.
  </Card>

  <Card title="Store and use secrets" icon="lock" href="/docs/guides/store-and-use-secrets">
    How secret interpolation works everywhere else.
  </Card>
</CardGroup>


## OpenAPI

````yaml post /v1/workspaces/{workspaceId}/widget_sessions
openapi: 3.1.0
info:
  title: Cadenya API
  description: API for the Cadenya Agent Runtime platform.
  version: '1.0'
servers:
  - url: https://api.cadenya.com
    description: Production server
security:
  - bearerAuth: []
tags:
  - name: AIProviderKeyService
  - name: APIKeyService
    description: |-
      Issue, rotate, disable, and revoke a workspace's API keys. Every key
       belongs to exactly one workspace; the system-managed global account key is
       managed via GlobalAPIKeyService instead.
  - name: AccountService
    description: >-
      Manage the authenticated account. Accounts are the top-level
      organizational
       unit and contain one or more workspaces.
  - name: AgentScheduleService
    description: >-
      Manage recurring schedules attached to agents. Schedules trigger
      objectives
       on a cadence defined by AgentScheduleSpec.Schedule.
  - name: AgentService
    description: >-
      Manage AI agents within a workspace. Agents define AI behavior and tool
      access.
  - name: AgentVariationService
    description: >-
      Manage variations of an agent and their tool, sub-agent, and memory layer
      assignments.
  - name: GlobalAPIKeyService
    description: |-
      Manage the account's system-provisioned global API key. The global key is
       the only key that spans every workspace; it is created by the system and
       cannot be deleted, so the surface is retrieve, rotate, and the
       disable/enable kill switch.
  - name: MemoryService
    description: >-
      Manage memory layers and their entries. Layers are named containers that
      can
       be composed into an objective's memory cascade; entries are the keyed values
       within a layer. System-managed layers (e.g., episodic layers created by the
       runtime) cannot be mutated through this API.
  - name: ModelService
    description: |-
      Manage LLM models available to a workspace. Models represent provider and
       family pairs (e.g., "anthropic/claude-sonnet-4.6"). Workspaces are seeded
       with the supported models and you can enable or disable each one.
  - name: ObjectiveEventStreamsService
  - name: ObjectiveService
  - name: ProfilesService
    description: |-
      Operations on profiles, the account-level principals (users, API keys,
       system) that authenticate against the API.
  - name: SearchService
  - name: TenantService
    description: >-
      Read and erase tenants and the subjects under them. Tenants and subjects
      are
       created by assertion — on objective creation or widget session mint — never
       directly, so this service has no create or update: it exists to enumerate what
       assertions have produced, and to destroy it on request.
  - name: ToolService
    description: >-
      Manage tool sets and the tools they contain. Tool sets group related
      tools,
       and tools define specific capabilities available to agents.

       When a tool set is managed, only API key actors can modify its tools; human
       (profile) actors cannot.
  - name: UploadService
    description: |-
      Issue short-lived presigned URLs for direct client-to-object-storage
       uploads. Created uploads can be referenced by id when creating or updating
       resources that accept binary content (e.g., MemoryEntry).
  - name: WidgetService
    description: |-
      Manage embeddable chat widgets. A widget binds an agent to a globally
       unique hostname with a per-widget origin allowlist; browsers reach it with
       session tokens minted via WidgetSessionService.
  - name: WidgetSessionService
    description: >-
      Mint and manage widget sessions. Session creation is server-to-server
      only:
       the customer's backend authenticates its visitor, asserts tenant/subject
       context, attaches any per-visitor secrets, and receives a short-lived
       bearer token the browser uses against the widget host.
  - name: WorkspaceAdminService
    description: >-
      Administer workspaces across the account: create and archive workspaces
      and
       manage their membership. These operations are account-scoped and require the
       admin role (a token whose profile holds the WorkOS admin role); they live
       under /v1/account/workspaces rather than the workspace-scoped /v1/workspaces
       tree so an admin can manage any workspace in the account, including ones they
       are not themselves a member of.
  - name: WorkspaceSecretService
  - name: WorkspaceService
    description: |-
      Manage workspaces within an account. Workspaces provide organizational
       grouping and isolation for resources such as agents, tools, and API keys.

       This is the workspace-scoped, end-user surface. Administrative operations
       (create / archive workspaces, manage members) live in WorkspaceAdminService
       under /v1/account/workspaces and require the admin role.
paths:
  /v1/workspaces/{workspaceId}/widget_sessions:
    post:
      tags:
        - WidgetSessionService
        - Widget Sessions
      summary: Create a widget session
      description: >-
        Mints a session against a widget and returns the session bearer token
        (`spec.token`, returned only on creation) plus the authoritative widget
        hostname (`info.host`). Asserting a tenant upserts the tenant record;
        attached secrets flow to every conversation the session creates.
      operationId: WidgetSessionService_CreateWidgetSession
      parameters:
        - name: workspaceId
          in: path
          description: Workspace ID.
          required: true
          schema:
            type: string
            example: workspace_01HXKD2E5NQM3T9AYWCF133E3Q
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWidgetSessionRequest'
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WidgetSession'
        default:
          description: Default error response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
      x-codeSamples:
        - lang: JavaScript
          source: |-
            import Cadenya from '@cadenya/cadenya';

            const client = new Cadenya({
              apiKey: process.env['CADENYA_API_KEY'], // This is the default and can be omitted
            });

            const widgetSession = await client.widgetSessions.create({
              workspaceId: 'workspace_01HXKD2E5NQM3T9AYWCF133E3Q',
              spec: { widgetId: 'wgt_01HXKD2E5NQM3T9AYWCFMZZZBD' },
            });

            console.log(widgetSession.metadata);
        - lang: Python
          source: |-
            import os
            from cadenya import Cadenya

            client = Cadenya(
                api_key=os.environ.get("CADENYA_API_KEY"),  # This is the default and can be omitted
            )
            widget_session = client.widget_sessions.create(
                workspace_id="workspace_01HXKD2E5NQM3T9AYWCF133E3Q",
                spec={
                    "widget_id": "wgt_01HXKD2E5NQM3T9AYWCFMZZZBD"
                },
            )
            print(widget_session.metadata)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"go.cadenya.com/cadenya-go\"\n\t\"go.cadenya.com/cadenya-go/option\"\n)\n\nfunc main() {\n\tclient := cadenya.NewClient(\n\t\toption.WithAPIKey(\"My API Key\"),\n\t)\n\twidgetSession, err := client.WidgetSessions.New(context.TODO(), cadenya.WidgetSessionNewParams{\n\t\tWorkspaceID: cadenya.String(\"workspace_01HXKD2E5NQM3T9AYWCF133E3Q\"),\n\t\tSpec: cadenya.WidgetSessionSpecParam{\n\t\t\tWidgetID: \"wgt_01HXKD2E5NQM3T9AYWCFMZZZBD\",\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", widgetSession.Metadata)\n}\n"
        - lang: Ruby
          source: |-
            require "cadenya"

            cadenya = Cadenya::Client.new(api_key: "My API Key")

            widget_session = cadenya.widget_sessions.create(
              workspace_id: "workspace_01HXKD2E5NQM3T9AYWCF133E3Q",
              spec: {widgetId: "wgt_01HXKD2E5NQM3T9AYWCFMZZZBD"}
            )

            puts(widget_session)
        - lang: CLI
          source: |-
            cadenya widget-sessions create \
              --api-key 'My API Key' \
              --workspace-id workspace_01HXKD2E5NQM3T9AYWCF133E3Q \
              --spec '{widgetId: wgt_01HXKD2E5NQM3T9AYWCFMZZZBD}'
components:
  schemas:
    CreateWidgetSessionRequest:
      required:
        - workspaceId
        - spec
      type: object
      properties:
        workspaceId:
          readOnly: true
          example: workspace_01HXKD2E5NQM3T9AYWCF133E3Q
          type: string
          description: Workspace ID.
        metadata:
          $ref: '#/components/schemas/CreateOperationMetadata'
        spec:
          $ref: '#/components/schemas/WidgetSessionSpec'
        secrets:
          type: array
          items:
            $ref: '#/components/schemas/CreateWidgetSessionRequest_Secret'
          description: Secrets to attach to the session.
      description: Create widget session request.
    WidgetSession:
      required:
        - metadata
        - spec
        - state
      type: object
      properties:
        metadata:
          $ref: '#/components/schemas/OperationMetadata'
        spec:
          $ref: '#/components/schemas/WidgetSessionSpec'
        info:
          $ref: '#/components/schemas/WidgetSessionInfo'
        state:
          readOnly: true
          enum:
            - STATE_UNSPECIFIED
            - STATE_ACTIVE
            - STATE_EXPIRED
            - STATE_REVOKED
            - STATE_EXHAUSTED
          type: string
          description: >-
            The current lifecycle state of the session. Output only. Sessions
            are
             created STATE_ACTIVE; use :revoke to end one early.
          format: enum
        secrets:
          readOnly: true
          type: array
          items:
            $ref: '#/components/schemas/WidgetSession_Secret'
          description: |-
            Names of the secrets attached to the session. Values are write-only:
             provided at creation, encrypted at rest, and interpolated into tool-call
             headers server-side — never returned by any API.
      description: >-
        WidgetSession is a delegated, narrowed credential for one visitor's use
        of
         a widget, minted server-to-server by the customer's backend. The session
         carries all customer-asserted context — tenant, subject, labels, secrets —
         and every conversation (objective) created through the widget inherits it.
         The bearer token returned at mint is short-lived and refreshed at the
         widget host; the session row is what makes revocation possible.
    Status:
      type: object
      properties:
        code:
          type: integer
          description: >-
            The status code, which should be an enum value of
            [google.rpc.Code][google.rpc.Code].
          format: int32
        message:
          type: string
          description: >-
            A developer-facing error message, which should be in English. Any
            user-facing error message should be localized and sent in the
            [google.rpc.Status.details][google.rpc.Status.details] field, or
            localized by the client.
        details:
          type: array
          items:
            $ref: '#/components/schemas/GoogleProtobufAny'
          description: >-
            A list of messages that carry the error details.  There is a common
            set of message types for APIs to use.
      description: >-
        The `Status` type defines a logical error model that is suitable for
        different programming environments, including REST APIs and RPC APIs. It
        is used by [gRPC](https://github.com/grpc). Each `Status` message
        contains three pieces of data: error code, error message, and error
        details. You can find out more about this error model and how to work
        with it in the [API Design
        Guide](https://cloud.google.com/apis/design/errors).
    CreateOperationMetadata:
      type: object
      properties:
        labels:
          type: object
          additionalProperties:
            type: string
          description: |-
            Key-value pairs for categorization and filtering. Values are 0-63
             alphanumeric characters with "-", "_", or "." allowed between; keys
             follow the same shape and additionally accept an optional DNS-subdomain
             prefix (e.g. "cadenya.com/") of at most 253 characters.
             Examples: {"priority": "high", "source": "api", "workflow": "onboarding"}
        externalId:
          type: string
          description: >-
            External ID for the operation (e.g., a workflow ID from an external
            system)
      description: |-
        CreateOperationMetadata contains the user-provided fields for creating
         an operation. Read-only fields (id, account_id, workspace_id, created_at, profile_id)
         are excluded since they are set by the server.
    WidgetSessionSpec:
      required:
        - widgetId
      type: object
      properties:
        widgetId:
          example: wgt_01HXKD2E5NQM3T9AYWCFMZZZBD
          type: string
          description: >-
            Widget this session is minted against. Accepts the canonical `wgt_…`
            form
             or the `external_id:<value>` form.
        tenant:
          allOf:
            - $ref: '#/components/schemas/TenantAssertion'
          description: >-
            Optional tenant assertion — the customer's org/company identifier
            for the
             visitor. Upserts the tenant record in the workspace and tags the session
             and every conversation it creates. Conversation listing at the widget
             host is scoped to this tenant.
        subject:
          allOf:
            - $ref: '#/components/schemas/SubjectAssertion'
          description: >-
            Optional subject assertion — the visitor within the tenant (e.g.
            their
             user id in the customer's namespace). Requires `tenant`; a subject
             asserted without a tenant is rejected with InvalidArgument.
        expiresAt:
          type: string
          description: >-
            Hard session expiry. Tokens never outlive it; after it passes the
            session
             transitions to STATE_EXPIRED. Defaults to a server-chosen horizon when
             unset.
          format: date-time
        token:
          readOnly: true
          type: string
          description: >-
            The session bearer token. Returned only on creation — subsequent
            reads
             omit it. The token is short-lived; the widget refreshes it at the widget
             host without involving the customer's backend.
        tokenExpiresAt:
          readOnly: true
          type: string
          description: |-
            Expiry of the token returned in `token`. Distinct from `expires_at`,
             which bounds the session itself.
          format: date-time
        pinnedParameters:
          type: object
          additionalProperties:
            type: string
          description: >-
            Parameters forced onto tool calls made by this session's
            conversations.
             A pinned parameter is an overlay on a tool's JSON schema: the parameter
             is removed from what the LLM sees, and its value is always overwritten
             server-side with the pinned value — so the model cannot be tricked into
             calling a tool with a different id than the one the session was minted
             for (e.g. pin "workspaceId" for an OpenAPI tool with a
             /workspaces/{workspaceId} path). Flows to every objective the session
             creates.
      description: WidgetSessionSpec is the configuration of a session, fixed at mint.
    CreateWidgetSessionRequest_Secret:
      type: object
      properties:
        name:
          type: string
        value:
          type: string
      description: |-
        Secret is a named credential attached to the session — typically a token
         the customer's backend minted for the visitor, so the agent acts against
         their API as that subject. Values are captured at the boundary, encrypted
         at rest, appended to every conversation the session creates (re-synced on
         each turn), and never returned by any API. Session secrets take
         precedence over workspace and tool-set secrets of the same name.
    OperationMetadata:
      required:
        - id
        - accountId
        - workspaceId
        - profileId
        - createdAt
      type: object
      properties:
        id:
          readOnly: true
          type: string
          description: >-
            Unique identifier for the operation (prefixed ULID, e.g.,
            "obj_01HXK...")
        accountId:
          readOnly: true
          example: account_01HXKD2E5NQM3T9AYWCFTJHJVF
          type: string
          description: >-
            Account this operation belongs to for multi-tenant isolation
            (prefixed ULID)
        workspaceId:
          readOnly: true
          example: workspace_01HXKD2E5NQM3T9AYWCF133E3Q
          type: string
          description: >-
            Workspace this operation belongs to for organizational grouping
            (prefixed ULID)
        labels:
          type: object
          additionalProperties:
            type: string
          description: |-
            Key-value pairs for categorization and filtering. Values are 0-63
             alphanumeric characters with "-", "_", or "." allowed between; keys
             follow the same shape and additionally accept an optional DNS-subdomain
             prefix (e.g. "cadenya.com/") of at most 253 characters.
             Examples: {"priority": "high", "source": "api", "workflow": "onboarding"}
        createdAt:
          readOnly: true
          type: string
          description: |-
            Timestamp when this operation was created
             ULID includes timestamp information, but this explicit field enables easier querying
          format: date-time
        externalId:
          type: string
          description: >-
            External ID for the operation (e.g., a workflow ID from an external
            system)
        profileId:
          readOnly: true
          example: profile_01HXKD2E5NQM3T9AYWCFS0AP08
          type: string
          description: >-
            ID of the actor (user or service account) that created this
            operation
      description: >-
        Metadata for ephemeral operations and activities (e.g., objectives,
        executions, runs)
    WidgetSessionInfo:
      type: object
      properties:
        widget:
          readOnly: true
          allOf:
            - $ref: '#/components/schemas/BareMetadata'
          description: The widget this session belongs to.
        agent:
          readOnly: true
          allOf:
            - $ref: '#/components/schemas/BareMetadata'
          description: |-
            The agent serving this session. Copied from the widget at mint and
             immutable for the session's lifetime — re-pointing the widget's agent
             affects new sessions only.
        tenant:
          readOnly: true
          allOf:
            - $ref: '#/components/schemas/TenantReference'
          description: The resolved tenant record, when a tenant was asserted at mint.
        subject:
          readOnly: true
          allOf:
            - $ref: '#/components/schemas/SubjectReference'
          description: The resolved subject record, when a subject was asserted at mint.
        host:
          readOnly: true
          example: k7m2xq9fp4wn.widgets.cadenya.com
          type: string
          description: >-
            The widget hostname this session's tokens are bound to.
            Authoritative —
             clients must use this value rather than constructing the hostname.
        messageCount:
          readOnly: true
          type: integer
          description: >-
            Number of conversation messages created through this session,
            counted
             against the session's message cap.
          format: int32
        lastActiveAt:
          readOnly: true
          type: string
          description: |-
            When the session last created a conversation, sent a message, or
             refreshed a token.
          format: date-time
      description: >-
        WidgetSessionInfo provides read-only server-derived data about a
        session.
    WidgetSession_Secret:
      type: object
      properties:
        name:
          type: string
      description: |-
        Secret is the name-only echo of a secret attached to the session. Values
         are never returned.
    GoogleProtobufAny:
      type: object
      properties:
        '@type':
          type: string
          description: The type of the serialized message.
      additionalProperties: true
      description: >-
        Contains an arbitrary serialized message along with a @type that
        describes the type of the serialized message.
    TenantAssertion:
      required:
        - id
      type: object
      properties:
        id:
          example: acme-corp
          type: string
          description: >-
            The tenant identifier in the customer's namespace (e.g.
            "acme-corp").
             Stored as the tenant record's external_id; stable across requests.
        name:
          example: Acme Corp
          type: string
          description: >-
            Optional human-readable name for the tenant. Updates the tenant
            record's
             name on every assertion that provides it.
      description: >-
        TenantAssertion identifies a tenant in the customer's own namespace —
        their
         org, company, or team identifier for an end user. Asserting a tenant
         upserts the tenant record in the workspace (keyed on `id` as the tenant's
         external_id) and associates the created resource with it.
    SubjectAssertion:
      required:
        - id
      type: object
      properties:
        id:
          example: customer-user-42
          type: string
          description: >-
            The subject identifier in the customer's namespace (e.g. their user
            id).
             Stored as the subject record's external_id; unique within the tenant.
        name:
          example: Jane Doe
          type: string
          description: |-
            Optional human-readable name for the subject. Updates the subject
             record's name on every assertion that provides it.
      description: >-
        SubjectAssertion identifies a person within a tenant in the customer's
        own
         namespace — typically their user id. Asserting a subject upserts the
         subject record under the asserted tenant and associates the created
         resource with it. A subject assertion is only valid alongside a tenant
         assertion: subject identifiers are scoped to their tenant.
    BareMetadata:
      type: object
      properties:
        id:
          readOnly: true
          type: string
        name:
          readOnly: true
          type: string
          description: >-
            Human-readable name of the referenced resource, populated by the
            server
             on reads for convenience. Absent on references to resources that do not
             have a name (e.g., objective tasks).
      description: |-
        BareMetadata contains the minimal metadata for a resource: the ID and an
         optional human-readable name. These are used for reference fields where the
         full metadata (account scoping, timestamps, labels, external IDs) is not
         needed — e.g., the tool references inside an agent variation spec or the
         tools assigned to an objective. Both fields are server-populated; clients
         provide IDs through sibling fields rather than by constructing a
         BareMetadata themselves.
    TenantReference:
      required:
        - id
        - externalId
      type: object
      properties:
        id:
          readOnly: true
          example: tenant_01HXKD2E5NQM3T9AYWCF133E3Q
          type: string
          description: Cadenya's canonical tenant id.
        externalId:
          readOnly: true
          example: acme-corp
          type: string
          description: The tenant identifier in the customer's namespace, as asserted.
        name:
          readOnly: true
          example: Acme Corp
          type: string
          description: Human-readable name of the tenant, when one has been asserted.
      description: >-
        TenantReference is the read-only echo of a resource's tenant
        association,
         carrying both Cadenya's canonical id and the customer's own key.
    SubjectReference:
      required:
        - id
        - externalId
      type: object
      properties:
        id:
          readOnly: true
          example: subj_01HXKD2E5NQM3T9AYWCFQAZGFV
          type: string
          description: Cadenya's canonical subject id.
        externalId:
          readOnly: true
          example: customer-user-42
          type: string
          description: >-
            The subject identifier in the customer's namespace, as asserted.
            Unique
             within the subject's tenant.
        name:
          readOnly: true
          example: Jane Doe
          type: string
          description: Human-readable name of the subject, when one has been asserted.
      description: >-
        SubjectReference is the read-only echo of a resource's subject
        association,
         carrying both Cadenya's canonical id and the customer's own key.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

````