> ## 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 memory layer

> Give an agent knowledge it pulls on demand. Skills layers advertise what they hold; episodic layers let the agent write notes to itself.

Nothing in a memory layer lands in the context window up front. The agent reaches for what it needs, when it needs it. That is the whole design: you can hand an agent a thousand pages of policy without paying for a single token until it asks.

`spec.type` is required, and it decides everything about how the layer behaves.

## Two types, two mechanics

| Type                         | Who writes it                  | What the agent sees up front                              |
| ---------------------------- | ------------------------------ | --------------------------------------------------------- |
| `MEMORY_LAYER_TYPE_SKILLS`   | You, through the API           | A manifest: every entry's key and description, no bodies. |
| `MEMORY_LAYER_TYPE_EPISODIC` | The agent, with `store_memory` | A primer plus a table of what it has stored before.       |

A skills layer is a reference library you curate. An episodic layer is a diary the agent keeps. They are not interchangeable.

Either way the agent reads with the same two tools: `get_memory` to pull an entry by its exact key, and `search_memory` to find a key it does not know. Only an episodic layer grants `store_memory`.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const layer = await client.memoryLayers.create({
    workspaceId,
    metadata: { name: 'Support playbook', externalId: 'support-playbook' },
    spec: {
      type: 'MEMORY_LAYER_TYPE_SKILLS',
      description: 'How to handle refunds, escalations, and outages.',
    },
  });

  await client.memoryLayers.entries.create(layer.metadata.id, {
    workspaceId,
    metadata: { name: 'refunds' },
    spec: {
      type: 'content',
      key: 'policy/refunds',
      description: 'When a customer asks for a refund.', // the model reads this to decide
      content: 'Refunds under $50 need no approval. Above that, escalate to a manager.',
    },
  });
  ```

  ```go Go theme={null}
  layer, err := client.MemoryLayers.New(ctx, cadenya.MemoryLayerNewParams{
  	WorkspaceID: cadenya.String(workspaceID),
  	Metadata: shared.CreateResourceMetadataParam{
  		Name:       "Support playbook",
  		ExternalID: cadenya.String("support-playbook"),
  	},
  	Spec: cadenya.MemoryLayerSpecParam{
  		Type:        cadenya.MemoryLayerSpecTypeMemoryLayerTypeSkills,
  		Description: cadenya.String("How to handle refunds, escalations, and outages."),
  	},
  })

  _, err = client.MemoryLayers.Entries.New(ctx, layer.Metadata.ID,
  	cadenya.MemoryLayerEntryNewParams{
  		WorkspaceID: cadenya.String(workspaceID),
  		Metadata: shared.CreateResourceMetadataParam{
  			Name: "refunds",
  		},
  		Spec: cadenya.MemoryEntryCreateSpecUnionParam{
  			OfContent: &cadenya.MemoryEntryCreateSpecContentParam{
  				Type:        cadenya.MemoryEntryCreateSpecContentTypeContent,
  				Key:         cadenya.String("policy/refunds"),
  				Description: cadenya.String("When a customer asks for a refund."), // the model reads this to decide
  				Content:     "Refunds under $50 need no approval. Above that, escalate to a manager.",
  			},
  		},
  	})
  ```

  ```ruby Ruby theme={null}
  layer = cadenya.memory_layers.create(
    workspace_id: workspace_id,
    metadata: {name: "Support playbook", external_id: "support-playbook"},
    spec: {
      type: :MEMORY_LAYER_TYPE_SKILLS,
      description: "How to handle refunds, escalations, and outages."
    }
  )

  cadenya.memory_layers.entries.create(
    layer.metadata.id,
    workspace_id: workspace_id,
    metadata: {name: "refunds"},
    spec: {
      type: :content,
      key: "policy/refunds",
      # the model reads this to decide
      description: "When a customer asks for a refund.",
      content: "Refunds under $50 need no approval. Above that, escalate to a manager."
    }
  )
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.cadenya.com/v1/workspaces/${WORKSPACE_ID}/memory_layers" \
    -H "Authorization: Bearer ${CADENYA_API_KEY}" \
    -H "Content-Type: application/json" \
    -d '{
          "metadata": { "name": "Support playbook", "externalId": "support-playbook" },
          "spec": {
            "type": "MEMORY_LAYER_TYPE_SKILLS",
            "description": "How to handle refunds, escalations, and outages."
          }
        }'

  curl -X POST "https://api.cadenya.com/v1/workspaces/${WORKSPACE_ID}/memory_layers/external_id:support-playbook/entries" \
    -H "Authorization: Bearer ${CADENYA_API_KEY}" \
    -H "Content-Type: application/json" \
    -d '{
          "metadata": { "name": "refunds" },
          "spec": {
            "type": "content",
            "key": "policy/refunds",
            "description": "When a customer asks for a refund.",
            "content": "Refunds under $50 need no approval. Above that, escalate to a manager."
          }
        }'
  ```
</CodeGroup>

Both `metadata.name` and `spec.type` are enforced: omit either and the request fails with a `400`.

## Write the entry description for the model

An entry's `description` is the only thing the model sees before it decides whether to read the body, and it is the text `search_memory` matches against. It is not a note to your teammates. It is the "when to use this" line.

Write `When a customer asks for a refund`, not `Refund policy v3 (updated by Dana)`. The first tells a model when to reach for the entry, and gives search something to hit. The second tells it nothing it can act on.

Search matters more than it looks. `search_memory` runs trigram similarity over keys and descriptions **only**, never over content. An entry whose body is full of the right words but whose description is vague stays invisible to the agent.

The entry `key` is what the model passes to `get_memory`. Slashes are conventional, not structural: `policy/refunds` reads like a path but lookups are flat, and the key is one opaque string.

<Warning>
  The `key` field documents rules that are not enforced today. Keys beginning or ending with `/`, containing `//`, or starting with the reserved `cadenya/` and `system/` prefixes are all accepted. Uniqueness within a layer **is** enforced, with a `409`. Do not rely on the rest to catch a malformed key for you.
</Warning>

## Content comes back only on a detail read

Listing entries gives you the summary view: `key`, `description`, and metadata. Read a single entry and you get a top-level `content` field alongside it. That keeps a list of a thousand entries from dragging their bodies along.

```typescript theme={null}
const entries = await client.memoryLayers.entries.list(layerId, { workspaceId });
for await (const entry of entries) {
  console.log(entry.spec.key, entry.spec.description); // no body here
}

const detail = await client.memoryLayers.entries.retrieve(layerId, entryId, { workspaceId });
console.log(detail.content); // the body
```

For a body too large to inline, upload it first and set `spec.type` to `uploadId` with the upload's ID, instead of `content` with an inline body. The `type` field names which source you chose, and the matching field must ride along with it.

<Warning>
  The `uploadId` path does not work today. An [upload](/docs/api-reference/uploadservice/create-an-upload) never leaves `UPLOAD_STATUS_PENDING`, and a memory entry that references one hangs rather than erroring. Keep bodies inline until that is fixed.
</Warning>

Sizes to keep in mind. An inline `content` caps at 1 MiB. A single `get_memory` call returns at most 500 lines or 100 KB, and the agent pages through anything longer. So a 1 MiB entry is legal and slow to read: split it into entries the agent can pick between.

## The cascade decides who wins

An objective resolves keys against an ordered list of layers. The first layer holding a key wins, and everything behind it is shadowed. Think CSS specificity: the most specific source takes the key.

Order, most specific first:

1. **The episodic layer**, when the objective carries an episodic key.
2. **The objective's `memoryCascade`**, in array order. Earlier elements are more specific.
3. **The variation's assigned layers**, by ascending `position`. Lower position is more specific.

```typescript theme={null}
await client.objectives.create({
  workspaceId,
  agentId: 'external_id:support',
  systemPromptData: {},
  firstUserMessage: 'The customer wants a refund.',
  memoryCascade: [
    { memoryLayerId: 'external_id:enterprise-playbook' }, // consulted first
    { memoryLayerId: 'external_id:support-playbook' },    // fallback
  ],
});
```

Give both layers a `policy/refunds` entry and the enterprise one wins, because it sits earlier in the array.

You never have to reason about this from memory. Read the objective back with `includeInfo` and `info.effectiveMemoryCascade` shows you the resolved order, index 0 first:

```typescript theme={null}
const objective = await client.objectives.retrieve(objectiveId, { workspaceId, includeInfo: true });
console.log(objective.info?.effectiveMemoryCascade);
// [ { memoryLayerId: 'memlyr_...enterprise' }, { memoryLayerId: 'memlyr_...support' } ]
```

Pin a single entry rather than a whole layer by passing `memoryEntryId` alongside its `memoryLayerId`. That entry then behaves as a one-entry layer at that position. The entry must belong to the layer you name.

The total effective cascade, your `memoryCascade` plus the variation's assignments, caps at **10 entries**. A request that would exceed it is rejected.

<Note>
  System-managed layers cannot be named in `memoryCascade`. The episodic layer attaches itself, at the most specific end, whenever an objective carries an episodic key.
</Note>

## Episodic memory: the agent's own notes

An episodic layer is not something you create and fill. Turn on `enableEpisodicMemory` on the agent, then give each objective an `episodicMemory.key`. Objectives sharing that key, for that agent, share one system-managed layer, and the agent writes into it with `store_memory`.

```typescript theme={null}
await client.objectives.create({
  workspaceId,
  agentId: 'external_id:support',
  systemPromptData: {},
  firstUserMessage: 'Pick up where we left off.',
  episodicMemory: { key: `customer-${customerId}` },
});
```

Every objective for that customer now reads and writes the same memory. The agent remembers the last conversation without you threading a transcript through your application. Set `episodicMemoryTtl` on the agent and the expiry slides forward each time a new objective touches the key. Leave the TTL unset and memories are kept indefinitely.

The flag and the key are a matched pair, and the API enforces both directions. Pass an `episodicMemory.key` to an agent without `enableEpisodicMemory` and the request fails. Omit the key for an agent that has it enabled, and it fails too.

Because the episodic layer sits at the most specific end of the cascade, what the agent learned about *this* customer beats whatever the general playbook says.

<Note>
  Episodic layers are system-managed. They appear in `list` (filter by `type`, `agentId`, or `episodicKeyPrefix`), but you cannot attach one to a variation, name one in `memoryCascade`, or edit it. It attaches itself.
</Note>

## Lifecycle

```typescript theme={null}
await client.memoryLayers.update(layerId, { workspaceId, spec: { description: 'Updated.' }, updateMask: 'spec.description' });
await client.memoryLayers.delete(layerId, { workspaceId });
```

Entries follow the same create, retrieve, list, update, delete shape, nested under their layer.

## Related

<CardGroup cols={2}>
  <Card title="Memory cascade" icon="layer-group" href="/docs/guides/memory-layers">
    How layers stack, and what happens on a key clash.
  </Card>

  <Card title="Give your agent memory" icon="brain" href="/docs/guides/give-your-agent-memory">
    The hands-on lesson, from empty layer to an agent that remembers.
  </Card>

  <Card title="Create an objective" icon="bullseye" href="/docs/api-reference/objectiveservice/create-a-new-objective">
    Where `memoryCascade` and `episodicMemory` get set per run.
  </Card>

  <Card title="Preventing tool bloat" icon="magnifying-glass" href="/docs/guides/preventing-tool-bloat">
    The same on-demand idea, applied to tools instead of knowledge.
  </Card>
</CardGroup>


## OpenAPI

````yaml post /v1/workspaces/{workspaceId}/memory_layers
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}/memory_layers:
    post:
      tags:
        - MemoryService
        - Memory Layers
      summary: Create a new memory layer
      description: Creates a new memory layer in the workspace
      operationId: MemoryService_CreateMemoryLayer
      parameters:
        - name: workspaceId
          in: path
          required: true
          schema:
            type: string
            example: workspace_01HXKD2E5NQM3T9AYWCF133E3Q
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateMemoryLayerRequest'
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MemoryLayer'
        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 memoryLayer = await client.memoryLayers.create({
              workspaceId: 'workspace_01HXKD2E5NQM3T9AYWCF133E3Q',
              metadata: { name: 'name' },
              spec: { type: 'MEMORY_LAYER_TYPE_UNSPECIFIED' },
            });

            console.log(memoryLayer.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
            )
            memory_layer = client.memory_layers.create(
                workspace_id="workspace_01HXKD2E5NQM3T9AYWCF133E3Q",
                metadata={
                    "name": "name"
                },
                spec={
                    "type": "MEMORY_LAYER_TYPE_UNSPECIFIED"
                },
            )
            print(memory_layer.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\t\"go.cadenya.com/cadenya-go/shared\"\n)\n\nfunc main() {\n\tclient := cadenya.NewClient(\n\t\toption.WithAPIKey(\"My API Key\"),\n\t)\n\tmemoryLayer, err := client.MemoryLayers.New(context.TODO(), cadenya.MemoryLayerNewParams{\n\t\tWorkspaceID: cadenya.String(\"workspace_01HXKD2E5NQM3T9AYWCF133E3Q\"),\n\t\tMetadata: shared.CreateResourceMetadataParam{\n\t\t\tName: \"name\",\n\t\t},\n\t\tSpec: cadenya.MemoryLayerSpecParam{\n\t\t\tType: cadenya.MemoryLayerSpecTypeMemoryLayerTypeUnspecified,\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", memoryLayer.Metadata)\n}\n"
        - lang: Ruby
          source: |-
            require "cadenya"

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

            memory_layer = cadenya.memory_layers.create(
              workspace_id: "workspace_01HXKD2E5NQM3T9AYWCF133E3Q",
              metadata: {name: "name"},
              spec: {type: :MEMORY_LAYER_TYPE_UNSPECIFIED}
            )

            puts(memory_layer)
        - lang: CLI
          source: |-
            cadenya memory-layers create \
              --api-key 'My API Key' \
              --workspace-id workspace_01HXKD2E5NQM3T9AYWCF133E3Q \
              --metadata '{name: name}' \
              --spec '{type: MEMORY_LAYER_TYPE_UNSPECIFIED}'
components:
  schemas:
    CreateMemoryLayerRequest:
      required:
        - metadata
        - spec
      type: object
      properties:
        workspaceId:
          readOnly: true
          example: workspace_01HXKD2E5NQM3T9AYWCF133E3Q
          type: string
        metadata:
          $ref: '#/components/schemas/CreateResourceMetadata'
        spec:
          $ref: '#/components/schemas/MemoryLayerSpec'
    MemoryLayer:
      required:
        - metadata
        - spec
      type: object
      properties:
        metadata:
          $ref: '#/components/schemas/ResourceMetadata'
        spec:
          $ref: '#/components/schemas/MemoryLayerSpec'
        info:
          $ref: '#/components/schemas/MemoryLayerInfo'
      description: >-
        MemoryLayer is a named container of memory entries that can be composed
        into
         an objective's memory cascade. Layers are workspace-scoped resources. The layer
         type controls how its entries participate in the agent loop — see
         MemoryLayerType for details.

         See "Memory cascade composition" above for how layers compose at lookup time.
    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).
    CreateResourceMetadata:
      required:
        - name
      type: object
      properties:
        name:
          type: string
          description: >-
            Human-readable name for the resource (e.g., "Customer Support
            Agent", "Email Tool")
        externalId:
          type: string
          description: >-
            External ID for the resource (e.g., a workflow ID from an external
            system)
        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: {"environment": "production", "team": "platform", "version": "v2"}
      description: |-
        CreateResourceMetadata contains the user-provided fields for creating
         a workspace-scoped resource. Read-only fields (id, account_id, workspace_id, profile_id,
         created_at) are excluded since they are set by the server.
    MemoryLayerSpec:
      required:
        - type
      type: object
      properties:
        type:
          enum:
            - MEMORY_LAYER_TYPE_UNSPECIFIED
            - MEMORY_LAYER_TYPE_EPISODIC
            - MEMORY_LAYER_TYPE_SKILLS
          type: string
          format: enum
        description:
          type: string
          description: |-
            Human-readable description of the layer's purpose. Encouraged for
             user-created layers; system-managed layers may have a generated description.
        systemManaged:
          readOnly: true
          type: boolean
          description: >-
            Server-set. True for layers managed by the system (e.g., episodic
            layers
             created automatically when an objective uses an episodic_key). System-managed
             layers cannot be assigned to objective cascades via the API and cannot be
             mutated by clients — their lifecycle is controlled entirely by the runtime.
        expiresAt:
          readOnly: true
          type: string
          description: >-
            For layers with a finite lifetime (e.g., episodic), the time at
            which the
             layer becomes eligible for cleanup. Set by the system; unset for
             persistent layers.
          format: date-time
        agentId:
          readOnly: true
          example: agent_01HXKD2E5NQM3T9AYWCFMGWT9Y
          type: string
          description: >-
            Server-set on episodic layers: the agent this layer belongs to.
            Unset for
             non-episodic layers.
        episodicKey:
          readOnly: true
          type: string
          description: >-
            Server-set on episodic layers: the caller-supplied episodic key the
            layer
             was created for. Unset for non-episodic layers.
    ResourceMetadata:
      required:
        - id
        - accountId
        - workspaceId
        - name
        - profileId
        - createdAt
      type: object
      properties:
        id:
          readOnly: true
          type: string
          description: >-
            Unique identifier for the resource (prefixed ULID, e.g.,
            "agent_01HXK...")
        accountId:
          readOnly: true
          example: account_01HXKD2E5NQM3T9AYWCFTJHJVF
          type: string
          description: >-
            Account this resource belongs to for multi-tenant isolation
            (prefixed ULID)
        workspaceId:
          readOnly: true
          example: workspace_01HXKD2E5NQM3T9AYWCF133E3Q
          type: string
          description: >-
            Workspace this resource belongs to for organizational grouping
            (prefixed ULID)
        name:
          type: string
          description: >-
            Human-readable name for the resource (e.g., "Customer Support
            Agent", "Email Tool")
             Required for resources that users interact with directly
        externalId:
          type: string
          description: >-
            External ID for the resource (e.g., a workflow ID from an external
            system)
        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: {"environment": "production", "team": "platform", "version": "v2"}
        profileId:
          readOnly: true
          example: profile_01HXKD2E5NQM3T9AYWCFS0AP08
          type: string
          description: ID of the actor (user or service account) that created this resource
        createdAt:
          readOnly: true
          type: string
          description: Timestamp when this resource was created
          format: date-time
        updatedAt:
          readOnly: true
          type: string
          description: Timestamp when this resource was last updated
          format: date-time
      description: >-
        Standard metadata for persistent, named resources (e.g., agents, tools,
        prompts)
    MemoryLayerInfo:
      type: object
      properties:
        entryCount:
          readOnly: true
          type: integer
          description: Number of entries currently in this layer.
          format: int32
        createdBy:
          $ref: '#/components/schemas/Profile'
        lastUsedAt:
          readOnly: true
          type: string
          description: >-
            Timestamp of the most recent objective that resolved against this
            layer.
             Useful for surfacing unused layers in the dashboard.
          format: date-time
        agent:
          readOnly: true
          allOf:
            - $ref: '#/components/schemas/ResourceMetadata'
          description: |-
            For episodic layers, the metadata of the agent the layer belongs to
             (resolved from MemoryLayerSpec.agent_id). Unset for non-episodic layers.
    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.
    Profile:
      required:
        - metadata
        - spec
      type: object
      properties:
        metadata:
          $ref: '#/components/schemas/AccountResourceMetadata'
        spec:
          $ref: '#/components/schemas/ProfileSpec'
      description: |-
        A profile identifies a user or non-human principal (such as an API key)
         at the account level. Profiles are account-scoped and can be granted access
         to multiple workspaces.
    AccountResourceMetadata:
      required:
        - id
        - accountId
        - name
        - profileId
      type: object
      properties:
        id:
          readOnly: true
          type: string
          description: >-
            Unique identifier for the resource (prefixed ULID, e.g.,
            "apikey_01HXK...")
        accountId:
          readOnly: true
          example: account_01HXKD2E5NQM3T9AYWCFTJHJVF
          type: string
          description: >-
            Account this resource belongs to for multi-tenant isolation
            (prefixed ULID)
        name:
          type: string
          description: >-
            Human-readable name for the resource (e.g., "Customer Support
            Agent", "Email Tool")
             Required for resources that users interact with directly
        externalId:
          type: string
          description: >-
            External ID for the resource (e.g., a workflow ID from an external
            system)
        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: {"environment": "production", "team": "platform", "version": "v2"}
        profileId:
          readOnly: true
          example: profile_01HXKD2E5NQM3T9AYWCFS0AP08
          type: string
        createdAt:
          readOnly: true
          type: string
          format: date-time
      description: >-
        AccountResourceMetadata is used to represent a resource that is
        associated to an account but not to a workspace.
    ProfileSpec:
      required:
        - type
      type: object
      properties:
        email:
          type: string
          description: >-
            Email address of the profile. Required and unique within an account
            for
             user profiles.
        name:
          type: string
          description: Display name (e.g., "Bobby Tables").
        type:
          enum:
            - PROFILE_TYPE_UNSPECIFIED
            - PROFILE_TYPE_USER
            - PROFILE_TYPE_API_KEY
            - PROFILE_TYPE_SYSTEM
          type: string
          description: >-
            Whether this profile represents a human user, an API key, or a
            system
             principal.
          format: enum
      description: Configuration for a profile.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

````