# Internalize complete documentation

---

OpenAPI: https://convergingthought.com/openapi.json

---

# Activate an adapter

Source: https://convergingthought.com/docs/api-reference/activation

> POST /v1/adapters/activate — change future routing with an expected-version check.



Requires the `adapters` scope. This operation selects a retained version for its owning subject. It is synchronous and returns the resulting route revision; it does not start a training job.

## Customer scope [#customer-scope]

Supply optional `tenant_id` in the body or `X-Internalize-Tenant` in the headers. If both are present they must match. A tenant-restricted key supplies its own tenant when omitted and rejects another tenant. Without either, an unrestricted key uses the separate project namespace. Use the same tenant for learning, inference, job reads, and version changes. See [Serve multiple customers](/docs/guides/multitenancy).

## Request [#request]

```json
{
  "version_id": "CANDIDATE_VERSION_ID",
  "expected_version_id": "CURRENT_ACTIVE_VERSION_ID"
}
```

Both fields are required. `version_id` identifies an existing candidate in the authenticated project. `expected_version_id` is the subject's current active version, or JSON `null` if the subject is still on base. The string `"base"` is not a substitute for `null` in this field.

The subject is determined from the candidate's ownership. You cannot use this endpoint to attach another subject's adapter to a new subject, merge adapters, or change a candidate's owner. Do not send an extra `subject_id` field.

```bash
curl --fail-with-body "$INTERNALIZE_BASE_URL/v1/adapters/activate" \
  -H "Authorization: Bearer $INTERNALIZE_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
    "version_id": "CANDIDATE_VERSION_ID",
    "expected_version_id": null
  }'
```

## Successful response [#successful-response]

```json
{ "version_id": "CANDIDATE_VERSION_ID", "revision": 1 }
```

New requests admitted after activation resolve to this version. Requests already admitted continue using their original snapshot. The revision increments on a successful activation operation. Activating a version that is already active can still increment the revision if the expected version matches; use a read to avoid unnecessary writes.

No additional internalization fee is charged merely for changing the active route. The candidate's earlier training job and its settled charge remain part of history.

## Conflicts [#conflicts]

The operation compares the current active version with `expected_version_id` atomically. A mismatch returns `409 activation_conflict` and does not change routing. Read the subject again and review which update should win.

An automatic retry that simply substitutes the newest expected value would overwrite a concurrent decision without review. Prefer to surface the new current version and your proposed candidate to the release owner or apply a deliberate application policy.

## Network ambiguity [#network-ambiguity]

Manual activation does not use the model-job idempotency mechanism. If the network fails after you submit it, read the subject. If your target is active, the desired route exists; do not repeat the write just to obtain another receipt. If a different version is active, investigate the intervening change before retrying.

## Rollback and expiry [#rollback-and-expiry]

To roll back, use a retained older candidate as `version_id` and the current version as the expected value. The candidate must not be expired. An expired candidate returns `adapter_expired`; an unknown or foreign-project version returns `not_found`.

Rollback affects future inference and future learning parents. It does not erase newer candidates or reverse previous outputs. There is no public reset-to-base operation. See [Deploying updates](/docs/guides/rollouts) for release sequencing and [Retention troubleshooting](/docs/troubleshooting/knowledge) for an expired route.


---

# Chat Completions

Source: https://convergingthought.com/docs/api-reference/chat-completions

> OpenAI-compatible text and function calls, backed by durable inference jobs.



`POST /v1/chat/completions` requires `inference` scope. The same rate, reservations, adapter ownership checks, and settlement rules apply as [native inference](/docs/api-reference/inferences).

## Customer scope [#customer-scope]

Supply optional `tenant_id` in the body or `X-Internalize-Tenant` in the headers. If both are present they must match. A tenant-restricted key supplies its own tenant when omitted and rejects another tenant. Without either, an unrestricted key uses the separate project namespace. Use the same tenant for learning, inference, job reads, and version changes. See [Serve multiple customers](/docs/guides/multitenancy).

## Request [#request]

```json
{
  "model": "glm-5.3",
  "messages": [{ "role": "user", "content": "Hello!" }],
  "max_completion_tokens": 2048,
  "temperature": 0,
  "stream": false
}
```

| Field                          | Behavior                                                                       |
| ------------------------------ | ------------------------------------------------------------------------------ |
| `model`                        | Required; `glm-5.3` only                                                       |
| `messages`                     | 1–64 messages; text, assistant function calls, and completed tool-result turns |
| `subject_id`                   | Optional; routes through that subject's active adapter                         |
| `adapter_version`              | Optional retained version, or `base`; non-base pinning requires a subject      |
| `max_completion_tokens`        | 1–4,096, default 2,048; includes reasoning                                     |
| `max_tokens`                   | Legacy alias; do not send both limits                                          |
| `temperature`                  | 0–1, default 0                                                                 |
| `tools`                        | Up to 32 function definitions with unique names                                |
| `tool_choice`                  | `auto`, `none`, `required`, or a named function                                |
| `parallel_tool_calls`          | Boolean; false permits at most one returned call                               |
| `response_format`              | `text` or `json_object`; JSON is validated after generation                    |
| `stream`                       | False by default; true returns buffered SSE                                    |
| `stream_options.include_usage` | Adds a final usage chunk when streaming                                        |
| `n`                            | Only 1 is supported                                                            |

Unknown fields are rejected. `strict: true` tools, `json_schema`, images, audio, logprobs, and Responses-specific fields are unsupported. JSON validation is not constrained decoding; invalid JSON can yield a failed job with consumed inference tokens charged.

Each message's normalized text is bounded to 16,000 JavaScript string-length units. Serialized normalized messages plus tools must fit 64,000 units. The HTTP body limit is 100,000 bytes. Function argument strings have a 16,000-unit bound. The actual tokenizer context ceiling is also enforced before paid execution.

## Routing [#routing]

Omitting the subject always selects base, even if the project's `default` subject has learned versions. Use `subject_id` or `X-Internalize-Subject` for adapted inference. If both appear, their values must match. A missing or expired explicitly requested adapter fails; it does not silently fall back.

The subject comes from your server's authorization mapping. A key is project-scoped and can optionally be restricted to a tenant; it is not restricted to one subject. Pinned versions must belong to the key’s project, the selected tenant, and the supplied subject.

## Conversation and tools [#conversation-and-tools]

`system`, `developer`, `user`, `assistant`, and `tool` roles are accepted. Developer messages normalize to system messages. Content may be a string or an array of text blocks. Null assistant content is accepted when function calls are present.

Assistant calls need unique IDs and JSON-object argument strings. Every call must receive exactly one matching tool result before the next assistant generation. The final message must be a user message or the end of a completed tool-result block. The server emits function calls; your runtime authorizes and executes them.

## Response [#response]

A successful JSON response includes the standard `id`, `object`, `created`, `model`, `choices`, and `usage` fields. `choices[0].message` contains final text, `tool_calls`, or both. `finish_reason` is `stop`, `length`, or `tool_calls`.

The `internalize` extension contains `job_id`, `subject_id`, and the resolved `adapter_version`. Response headers include `X-Internalize-Job-Id`, `Idempotency-Key`, and `X-Request-Id`. Inference usage includes all generated reasoning tokens, but reasoning text is never returned.

## Stream behavior [#stream-behavior]

SSE sends keepalive comments while the durable job runs. After validation it emits a content/tool delta, a finish chunk, optional usage, and `[DONE]`. Tool deltas include their index, ID, function name, and complete argument string.

This stream is buffered until final validation, not token-live generation. Disconnecting or cancelling the client stream does not cancel the underlying job. The same job remains visible in Activity and through `/v1/jobs/{id}`.

## Recovery and deadlines [#recovery-and-deadlines]

A supplied `Idempotency-Key` is strongly recommended for paid operations. Reusing it with the same normalized payload and submitting credential returns the original job. A conflicting body or another key returns `409`. If omitted, the gateway generates an operation key and returns it in the response header.

The HTTP wait is bounded to about four minutes. A pending, failed, or reconciliation result after admission uses `409` and includes `error.job_id`, `error.status_url`, and `retryable: false`. Poll that existing job. A stream already admitted reports the same error envelope in a data frame before `[DONE]`.

Disable automatic framework retries unless they reuse a persisted operation key. If a connection fails before you save the generated key or job ID, a fresh request may create new paid work. Use [native asynchronous inference](/docs/api-reference/inferences) when your application needs a job receipt immediately.


---

# Conventions and limits

Source: https://convergingthought.com/docs/api-reference/conventions

> Headers, request IDs, idempotency, pagination, time, and input limits.



Use HTTPS and JSON with the production origin `https://convergingthought.com`. Endpoint paths begin with `/v1`. Request field names and identifiers are case-sensitive. Unknown fields in model request bodies are rejected rather than silently treated as provider options.

## Headers and receipts [#headers-and-receipts]

```http
Authorization: Bearer YOUR_PROJECT_KEY
Content-Type: application/json
Idempotency-Key: 4e4b8ce2-6309-49fb-8abe-76097b50717b
```

The idempotency header is required for inference and internalization. It is not required for reads or manual activation. Public JSON responses include `X-Request-Id` and use `Cache-Control: no-store`. The HTTP request ID identifies a single transport attempt; the job ID identifies durable work across attempts. Save both when debugging.

Accepted model operations return `{ id, status_url }`, with the same relative URL in the `Location` header. Resolve that URL against the platform origin. A list response uses `data`, while an individual job is returned directly; there is no universal `data` wrapper around every endpoint.

## Idempotency [#idempotency]

Keys contain 8–128 characters, begin with a letter or number, and otherwise allow letters, numbers, dots, underscores, colons, and hyphens. A UUID is suitable. Generate the key once per intended operation and store it before sending the request.

Idempotency keys are scoped to a project and tenant namespace, and shared across the two model-operation types within that namespace. Omitted tenant and an explicit matching tenant on a tenant-restricted key resolve to the same identity. Keep the original tenant and submitting API key for retries. Reusing a key for an inference after using it for an internalization is a conflict. Reusing it with changed content, subject, temperature, output limit, or activation choice is also a conflict.

The server compares the validated payload, including applied defaults. Keep the original payload in your application rather than relying on incidental serialization differences. An identical replay recovers the existing job without another reservation. There is no documented short expiration window after which you should deliberately reuse a key for unrelated work.

## Input limits [#input-limits]

| Item                  | Limit                                                             |
| --------------------- | ----------------------------------------------------------------- |
| JSON request body     | 100,000 bytes at the public body reader                           |
| Subject ID            | 1–128 allowed characters                                          |
| Learning content      | 20–16,000 string-length units                                     |
| Inference messages    | 1–64 messages                                                     |
| Each message content  | 1–16,000 string-length units                                      |
| Total message content | Serialized messages and tools: at most 64,000 string-length units |
| Output ceiling        | 1–4,096 tokens, including reasoning                               |
| Temperature           | 0–1 inclusive                                                     |
| Job list page         | 1–100 records; default 50                                         |

Text validation uses JavaScript string length at the public boundary, so some Unicode symbols occupy two units. Token limits and byte limits are different measurements. Leave room below the text cap when constructing multilingual payloads.

## Rate limits and retries [#rate-limits-and-retries]

New model admissions share a project-level limit of 60 per minute. The two operation types use the same fixed minute window. An identical accepted replay is resolved before the new-admission limiter. `429 rate_limited` includes `Retry-After: 60`.

Back off on rate limits and transient transport failures. For a model write, preserve the original key and payload. An HTTP `retryable` flag is advice about the request error, not permission to generate a new paid operation. For a job in reconciliation, stop automatic resubmission and inspect the original job.

## Pagination and time [#pagination-and-time]

Jobs are listed newest first. Pass `next_cursor` back unchanged as `cursor`, with the same endpoint. A `null` cursor means the end. Cursors are opaque; do not construct offsets or parse their contents. The subjects endpoint is a bounded listing rather than a paginated collection.

Public job and subject timestamps use ISO 8601 UTC strings. Billing calendar months and daily usage use UTC. The optional candidate expiry inside a job result may be a Unix timestamp in seconds; the subject listing's `expires_at` is an ISO timestamp. Parse fields according to their documented resource shape.


---

# Errors and retry decisions

Source: https://convergingthought.com/docs/api-reference/errors

> Distinguish rejected requests, failed jobs, and outcomes that need reconciliation.



An HTTP error means the current API request did not return a successful response. A job error describes an admitted operation. They are related but not interchangeable: a successful `GET /v1/jobs/{id}` can return a job whose status is `failed`.

## HTTP error envelope [#http-error-envelope]

```json
{
  "error": {
    "code": "insufficient_credits",
    "retryable": false,
    "request_id": "HTTP_REQUEST_ID"
  }
}
```

The request ID is also returned in `X-Request-Id`. Preserve it with the method, route, status, and timestamp. Avoid logging authorization headers or model content to diagnose an error.

## Error reference [#error-reference]

| HTTP | Code                      | Meaning and next action                                                                                                   |
| ---- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| 401  | `unauthorized`            | Missing, malformed, expired, or revoked key. Check the server credential.                                                 |
| 403  | `server_keys_only`        | The request carries an Origin header. Move the API call to your server.                                                   |
| 403  | `insufficient_scope`      | The key lacks the operation's scope. Use the correct credential.                                                          |
| 403  | `preview_only`            | Hosted model admission is disabled. Retrying cannot enable execution.                                                     |
| 402  | `insufficient_credits`    | Available balance cannot cover the reservation. Fund the project when live billing is enabled.                            |
| 402  | `spend_limit_exceeded`    | Current spend plus reservations would exceed the hard monthly limit. Review Settings.                                     |
| 402  | `project_frozen`          | The balance needs reconciliation, for example after a refund. Resolve billing.                                            |
| 403  | `tenant_scope_mismatch`   | The request selects a tenant outside the key’s fixed restriction. Use the authorized tenant or correct server credential. |
| 404  | `not_found`               | Unknown route or resource, or resource outside the key's project. Verify the ID and project.                              |
| 409  | `idempotency_conflict`    | The same key was used for different work. Recover the original payload or choose a new key for a genuinely new operation. |
| 409  | `activation_conflict`     | The expected active version no longer matches. Read current state and make a release decision.                            |
| 409  | `adapter_expired`         | Required adapter retention has expired. Inspect the route and contact support.                                            |
| 413  | `body_too_large`          | JSON body exceeds the byte limit. Reduce the request.                                                                     |
| 422  | `invalid_request`         | Invalid JSON, content type, header, field, or semantic constraint. Check the endpoint schema.                             |
| 429  | `rate_limited`            | New admission limit reached. Honor Retry-After and back off.                                                              |
| 503  | `platform_not_configured` | A required platform dependency is unavailable or unconfigured. Retry reads conservatively; preserve write identity.       |
| 500  | `internal_error`          | An unexpected server error occurred. Keep the request ID and recover writes idempotently.                                 |

Console-specific operations can also report `invalid_origin` or `billing_not_configured`. They are not reasons to change public API key scopes or bypass the console's session boundary.

## What retryable means [#what-retryable-means]

The public HTTP wrapper marks `rate_limited`, `platform_not_configured`, and `internal_error` as retryable. That flag describes whether a later transport attempt might succeed. It does not authorize a new idempotency key, prove the original work never started, or cancel a job.

A non-retryable admission error can become resolvable after a deliberate change. For example, adding real credit can resolve `insufficient_credits`. If no job was admitted and the intended body is unchanged, reuse its original idempotency key after fixing the cause.

For reads, use bounded exponential backoff with jitter. For model writes, retry only the same intent with the same key and body. Set a ceiling on attempts so a configuration failure does not become an endless loop.

## Job errors [#job-errors]

A job's `error` contains `code` and `retryable`; its ID already identifies the durable operation. Worker codes can indicate curriculum failure, teacher validation failure, expired state, dispatch uncertainty, or provider execution problems. Treat the code as diagnostic and inspect status and settlement before deciding what to do.

`reconciliation_required` needs investigation even if your client library throws an exception. Do not catch it in a generic retry loop that submits a new learning or inference job. Preserve the original ID and follow [job recovery](/docs/troubleshooting/jobs).

## Unknown codes [#unknown-codes]

Display a useful generic failure when a code is unrecognized, and preserve the code in restricted operational logs. Avoid branching on human-readable text or raw provider messages. The server intentionally returns bounded machine-readable errors rather than private provider detail.

Use [safe diagnostics](/docs/troubleshooting/diagnostics) to report a reproducible problem without including keys, prompts, source passages, or answers.


---

# API overview

Source: https://convergingthought.com/docs/api-reference

> The complete public HTTP surface for asynchronous GLM 5.3 inference and managed adapters.



The API lives at `https://convergingthought.com/v1`. Authenticate from your server with a project key. Submit model operations once, store their job IDs, and read the resulting state until the operation settles.

## Endpoints [#endpoints]

| Method | Path                                                           | Required scope | Result                                           |
| ------ | -------------------------------------------------------------- | -------------- | ------------------------------------------------ |
| POST   | [`/v1/internalizations`](/docs/api-reference/internalizations) | `internalize`  | Accepted learning job                            |
| POST   | [`/v1/inferences`](/docs/api-reference/inferences)             | `inference`    | Accepted inference job                           |
| GET    | [`/v1/jobs`](/docs/api-reference/jobs)                         | `read`         | Paginated job summaries                          |
| GET    | [`/v1/jobs/{id}`](/docs/api-reference/jobs)                    | `read`         | Complete public job record                       |
| GET    | [`/v1/subjects`](/docs/api-reference/subjects)                 | `read`         | Subjects, active versions, and recent candidates |
| POST   | [`/v1/adapters/activate`](/docs/api-reference/activation)      | `adapters`     | Activated version and revision                   |
| GET    | [`/v1/models`](/docs/api-reference/models)                     | Any valid key  | Supported model and published rates              |

The [OpenAPI document](/openapi.json) is generated from the public request and response schemas. Use it for tooling, then read the conventions and endpoint notes for constraints that require semantic checks, such as completing all tool results before the next model turn.

## First request [#first-request]

Read the model catalogue to verify the key without starting paid computation:

```bash
curl --fail-with-body https://convergingthought.com/v1/models \
  -H "Authorization: Bearer $INTERNALIZE_API_KEY"
```

For model writes, also send `Content-Type: application/json` and an `Idempotency-Key`. A new admission returns `202`; an exact replay returns `200` and the existing job ID. Both responses use the same receipt shape.

## Public boundaries [#public-boundaries]

There are no public endpoints for creating keys, buying credits, changing project settings, exporting weights, or deleting subjects. Use the authenticated console for the supported account operations. Private worker and console routes are not a substitute public API.

Streaming, synchronous completions, native tool-call messages, and customer callback URLs are not supported. Register a tool in your own agent runtime and let its server-side handler call these endpoints.

## Integration order [#integration-order]

Start with [authentication](/docs/getting-started/authentication), then implement [conventions and retries](/docs/api-reference/conventions), [job reads](/docs/api-reference/jobs), and [errors](/docs/api-reference/errors). Add inference and learning after your application can persist receipts and recover an interrupted wait. Check [preview availability](/docs/getting-started/availability) before expecting hosted execution.


---

# Create an inference

Source: https://convergingthought.com/docs/api-reference/inferences

> POST /v1/inferences — answer using the subject's active adapter and only the messages you send.



Requires the `inference` scope and an `Idempotency-Key`. Inference is asynchronous: the admission response contains a job ID, and the completed answer is retrieved from that job.

## Customer scope [#customer-scope]

Supply optional `tenant_id` in the body or `X-Internalize-Tenant` in the headers. If both are present they must match. A tenant-restricted key supplies its own tenant when omitted and rejects another tenant. Without either, an unrestricted key uses the separate project namespace. Use the same tenant for learning, inference, job reads, and version changes. See [Serve multiple customers](/docs/guides/multitenancy).

## Request [#request]

```json
{
  "subject_id": "support-policy",
  "messages": [
    {
      "role": "system",
      "content": "Answer clearly and do not invent policy details."
    },
    { "role": "user", "content": "Who pays for a return label?" }
  ],
  "max_output_tokens": 2048,
  "temperature": 0
}
```

| Field                | Type    | Default  | Constraint                                                                |
| -------------------- | ------- | -------- | ------------------------------------------------------------------------- |
| `subject_id`         | string  | Required | Valid subject ID                                                          |
| `messages`           | array   | Required | 1–64 messages; final turn must be a user or completed tool-result block   |
| `messages[].role`    | string  | Required | `system`, `user`, `assistant`, or `tool`                                  |
| `messages[].content` | string  | Required | At most 16,000 units; may be empty for tool-call messages or tool results |
| `max_output_tokens`  | integer | 2048     | 1–4096, including reasoning                                               |
| `temperature`        | number  | 0        | Between 0 and 1                                                           |

Serialized messages and tools together must fit 64,000 string-length units. Native messages use string content, including `tool` results and assistant `tool_calls`. Optional `tools`, `tool_choice`, `parallel_tool_calls`, and `response_format` follow the supported [Chat Completions fields](/docs/api-reference/chat-completions). `adapter_version` pins a retained version for this subject, or explicitly selects `base`. Omit it to use the active version. Images, audio, `model`, and `stream` are not accepted on this native endpoint.

## Context and memory [#context-and-memory]

Send the question, relevant instructions, and any conversation history the application needs. Internalize does not store an implicit conversation thread and append earlier messages for you. An `assistant` message in the array is history supplied by your application, not a request to generate an answer twice.

For knowledge already internalized into the active adapter, leave the original learning passage out of these messages. The adapter supplies learned behavior. If you reinsert the source, you are evaluating a context-assisted answer rather than source-free learned behavior.

## Routing [#routing]

At admission, the platform resolves the subject's current active version and records it on the job. If a new version activates a moment later, the already-admitted inference keeps its original snapshot.

A new subject starts from base. An existing subject whose active adapter has expired fails with `adapter_expired`; it does not silently start over. Use `adapter_version` to pin a retained version or clean base explicitly; there is no automatic fallback model.

## Response and completion [#response-and-completion]

```json
{ "id": "YOUR_JOB_ID", "status_url": "/v1/jobs/YOUR_JOB_ID" }
```

Poll until the job settles. For `status: "succeeded"`, read `result.text`, `result.finish_reason`, `adapter_version`, and usage. A finish reason can indicate the output ceiling was reached, so a transport-level success does not imply an untruncated answer. Inspect the result before displaying it as complete.

The output limit covers generated reasoning as well as visible answer tokens. The API does not return raw reasoning. A very low ceiling can leave little room for a final answer even when the visible response looks short.

## Cost and recovery [#cost-and-recovery]

Input tokens cost 12 micro-USD each, output including reasoning costs 30, and verified cached input costs 2.5. Cached tokens replace, rather than add to, ordinary input charges. Admission reserves a conservative input bound plus the maximum output allowance. Final settlement uses measured tokens and releases the unused reservation. If generation occurred but answer extraction failed, known usage can still be charged.

A failed HTTP connection does not prove no generation occurred. Recover with the same idempotency key and payload, then poll the original job. See [Billing](/docs/billing) for arithmetic and [Polling](/docs/sdk-reference/polling) for a durable integration pattern.


---

# Create an internalization

Source: https://convergingthought.com/docs/api-reference/internalizations

> POST /v1/internalizations — learn a passage into a new adapter candidate.



Requires the `internalize` scope and an `Idempotency-Key`. The request creates a durable learning job for one subject. It does not return a completed adapter synchronously.

## Customer scope [#customer-scope]

Supply optional `tenant_id` in the body or `X-Internalize-Tenant` in the headers. If both are present they must match. A tenant-restricted key supplies its own tenant when omitted and rejects another tenant. Without either, an unrestricted key uses the separate project namespace. Use the same tenant for learning, inference, job reads, and version changes. See [Serve multiple customers](/docs/guides/multitenancy).

## Request [#request]

```json
{
  "subject_id": "support-policy",
  "content": "Northstar accepts returns within 30 days of delivery. Return labels are prepaid. Items marked final sale cannot be returned.",
  "max_cost_microusd": 5000000,
  "activate": true
}
```

| Field               | Type    | Required | Behavior                                                |
| ------------------- | ------- | -------- | ------------------------------------------------------- |
| `subject_id`        | string  | Yes      | Stable subject in the authenticated project             |
| `content`           | string  | Yes      | Self-contained source, length 20–16,000                 |
| `max_cost_microusd` | integer | Yes      | Total budget, $1–$100, including initial hosting        |
| `activate`          | boolean | No       | Defaults to `true`; attempt activation after validation |

The content is a string, not a file upload, URL-fetch instruction, message array, or embedding. Include the actual knowledge and the definitions needed to interpret it. The API does not accept a model selector, training hyperparameters, custom validation arrays, or additional metadata fields.

## Example [#example]

```bash
curl --fail-with-body "$INTERNALIZE_BASE_URL/v1/internalizations" \
  -H "Authorization: Bearer $INTERNALIZE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: northstar-policy-release-001" \
  --data '{
    "subject_id": "support-policy",
    "content": "Northstar accepts returns within 30 days of delivery. Return labels are prepaid. Items marked final sale cannot be returned.",
    "max_cost_microusd": 5000000,
    "activate": false
  }'
```

Use `activate: false` when the candidate needs an explicit release decision. You can evaluate a retained candidate by pinning its `adapter_version` for the same tenant and subject before deciding to activate it.

## Admission response [#admission-response]

New work returns `202`. An identical idempotent replay returns `200`:

```json
{ "id": "YOUR_JOB_ID", "status_url": "/v1/jobs/YOUR_JOB_ID" }
```

The request reserves `max_cost_microusd`, a required integer from 1,000,000 to 100,000,000 ($1–$100). This caps compute and initial 24-hour hosting together. It captures the subject's active adapter as the parent, or `base` for a clean subject. The project must have sufficient available balance and monthly capacity before work is admitted.

## Result [#result]

Poll the job with the same learning key, or a project key with `read` scope. A successful saved candidate has `status: "ready"` and a result shaped like this illustrative example:

```json
{
  "candidate": { "version_id": "YOUR_VERSION_ID" },
  "validation": { "passed": true, "passed_count": 8, "total_count": 8 },
  "activated": true,
  "activation_conflict": false
}
```

`ready` is learning success, while `activated` is the routing outcome. If another activation changed the parent during training, the candidate is saved but does not automatically replace that newer state. Inspect the subject and decide whether to activate manually.

## Failures and charges [#failures-and-charges]

A failed validation produces `rejected` and leaves the active version unchanged. A known execution error produces `failed`; read `error.code`. There is no fixed fee. Completed compute is charged for rejected candidates, budget exhaustion, and known learning outcomes. Our infrastructure failures are waived. A ready candidate also incurs verified hosting, even if activation was disabled or conflicted; see [billing](/docs/billing).

An ambiguous outcome can produce `reconciliation_required`, with the reservation held until evidence is recovered. Do not submit the same learning intent with a new idempotency key. See [job recovery](/docs/troubleshooting/jobs).

The current hosted preview returns `execution_unavailable` before executing model work. For enabled projects, follow [knowledge preparation](/docs/guides/knowledge) and [evaluation](/docs/guides/evaluation) to judge the resulting behavior.


---

# Read and list jobs

Source: https://convergingthought.com/docs/api-reference/jobs

> GET /v1/jobs/{id} and GET /v1/jobs — durable outcomes, usage, and settlement.



Listing project jobs requires `read`. A key with `inference` or `internalize` can read individual jobs that it submitted under that scope. Other keys need `read` within the same selected tenant to inspect them. A job belongs to the project selected by the API key. Reading an unknown or another project's job returns `not_found` rather than disclosing cross-project state.

For customer-scoped jobs, repeat `X-Internalize-Tenant` on list and detail reads, or use a key restricted to that tenant. An omitted header on an unrestricted key selects only the project namespace. Responses include `tenant_id` (`null` for project memory); knowledge from another tenant is never returned. See [Serve multiple customers](/docs/guides/multitenancy).

## Read a job [#read-a-job]

```bash
curl --fail-with-body "$INTERNALIZE_BASE_URL/v1/jobs/YOUR_JOB_ID" \
  -H "Authorization: Bearer $INTERNALIZE_API_KEY"
```

The response is the job object directly. This example is illustrative, with placeholder identifiers and usage:

```json
{
  "id": "YOUR_JOB_ID",
  "subject_id": "support-policy",
  "kind": "inference",
  "status": "succeeded",
  "phase": "complete",
  "adapter_version": "YOUR_VERSION_ID",
  "usage": {
    "input_tokens": 120,
    "output_tokens": 80,
    "cached_input_tokens": 0,
    "training_tokens": 0
  },
  "billing": {
    "reserved_microusd": 25000,
    "charged_microusd": 3840,
    "settled": true,
    "pricing_version": "2026-09-27",
    "compute_microusd": 3840,
    "storage_microusd": 0
  },
  "result": { "text": "ILLUSTRATIVE_ANSWER", "finish_reason": "stop" },
  "error": null,
  "created_at": "2026-09-24T10:00:00.000Z",
  "updated_at": "2026-09-24T10:00:08.000Z"
}
```

## Fields [#fields]

| Field                       | Meaning                                                       |
| --------------------------- | ------------------------------------------------------------- |
| `id`                        | Durable job ID returned by admission                          |
| `subject_id`                | The subject name supplied by the caller                       |
| `kind`                      | `inference` or `internalization`                              |
| `status`                    | Machine-readable lifecycle state                              |
| `phase`                     | Diagnostic execution phase; do not treat it as a percentage   |
| `adapter_version`           | Version resolved at admission, or `base`                      |
| `usage.input_tokens`        | Recorded inference input tokens                               |
| `usage.output_tokens`       | Recorded inference output tokens, including reasoning         |
| `billing.reserved_microusd` | Original reservation, retained for inspection                 |
| `billing.charged_microusd`  | Settled charge; inspect `settled` before treating it as final |
| `billing.settled`           | Whether the reservation has been settled                      |
| `result`                    | Operation-specific result, or `null`                          |
| `error`                     | Job error code and retryability, or `null`                    |
| `created_at`, `updated_at`  | ISO UTC timestamps                                            |

For learning, `adapter_version` is the parent. The candidate appears under `result.candidate.version_id`. Validation uses `passed`, `passed_count`, and `total_count`; activation uses `activated` and `activation_conflict`.

Result objects can contain additional diagnostic fields. Read the known fields you need without assuming every operation has `text` or every unsuccessful job has a candidate. Do not expose an entire result object publicly without considering the model content it contains.

## Status handling [#status-handling]

| Status                    | Interpretation                             | Next step                          |
| ------------------------- | ------------------------------------------ | ---------------------------------- |
| `queued`                  | Admitted, waiting for execution            | Poll                               |
| `running`                 | Worker executing                           | Poll with backoff                  |
| `succeeded`               | Inference completed                        | Read the answer and settlement     |
| `ready`                   | Learning candidate saved and validated     | Inspect activation outcome         |
| `rejected`                | Learning validation rejected the candidate | Review source and evaluation       |
| `failed`                  | Known terminal error                       | Read error and settled charge      |
| `reconciliation_required` | Outcome or usage remains ambiguous         | Preserve ID; request investigation |

The public job error currently reports `retryable: false`. This is distinct from an HTTP error's retryability. Do not build a generic “retry all failures” button that resubmits paid work with a new key.

## List jobs [#list-jobs]

```bash
curl --fail-with-body --get "$INTERNALIZE_BASE_URL/v1/jobs" \
  -H "Authorization: Bearer $INTERNALIZE_API_KEY" \
  --data-urlencode "limit=50"
```

List items contain only `id`, `kind`, `status`, `phase`, `adapter_version`, and `created_at`. Fetch the individual job for subject, answer, validation, usage, or billing details.

```json
{ "data": [], "next_cursor": null }
```

Results are newest first. `limit` defaults to 50 and accepts integers from 1 to 100. When `next_cursor` is non-null, send it as the `cursor` query parameter using URL encoding. Continue until `null`. There are no public server-side kind, subject, date, or status filters in this endpoint.

Pagination is for discovery and history, not a replacement for saving admission receipts. Persist the job ID immediately and poll it directly. See [Requests in the console](/docs/product/requests) for the corresponding inspection interface.

## Price and usage details [#price-and-usage-details]

`usage.cached_input_tokens` is a subset of input tokens. `usage.training_tokens` counts every billed training exposure. Learning input/output include teacher, scoring, and evaluation calls. `billing.pricing_version` is fixed at admission; `compute_microusd` and `storage_microusd` break down the settled charge. Missing or inconsistent provider receipts leave the request in reconciliation.


---

# List models

Source: https://convergingthought.com/docs/api-reference/models

> GET /v1/models — discover the currently supported model and published rates.



This endpoint requires a valid project key but does not require a particular scope. It reads the catalogue without starting inference, training, or a credit reservation.

```bash
curl --fail-with-body "$INTERNALIZE_BASE_URL/v1/models" \
  -H "Authorization: Bearer $INTERNALIZE_API_KEY"
```

## Response [#response]

```json
{
  "data": [
    {
      "id": "glm-5.3",
      "pricing": {
        "version": "2026-09-27",
        "input_usd_per_million_tokens": 12,
        "output_usd_per_million_tokens": 30,
        "cached_input_usd_per_million_tokens": 2.5,
        "training_usd_per_million_tokens": 36,
        "storage_usd_per_gb_month": 0.3,
        "internalization_fee_usd": 0
      }
    }
  ]
}
```

GLM 5.3 is the only supported model in the current public contract. The catalogue uses a model identifier for discovery, but model-operation requests do not accept a `model` field. They use the platform's supported model and the subject's selected adapter.

## Rate fields [#rate-fields]

`pricing.version` identifies the immutable price card recorded on newly admitted requests. Input and output have separate rates; output includes reasoning. Cached input receives the lower rate only when verified in provider receipts. Learning uses sampling and training tokens at the listed rates, without a per-call fee. Hosting is prorated by retained checkpoint size and time. See [Credits and pricing](/docs/billing).


---

# List subjects and versions

Source: https://convergingthought.com/docs/api-reference/subjects

> GET /v1/subjects — inspect the active route and recent retained adapters.



Requires `read`. This endpoint returns subjects in the selected tenant of the authenticated project, each with its active version, revision, and recent adapter metadata. It does not expose provider checkpoint paths or source passages.

```bash
curl --fail-with-body "$INTERNALIZE_BASE_URL/v1/subjects" \
  -H "Authorization: Bearer $INTERNALIZE_API_KEY"
```

## Response [#response]

```json
{
  "data": [
    {
      "id": "support-policy",
      "active_version": "YOUR_VERSION_ID",
      "revision": 1,
      "versions": [
        {
          "id": "YOUR_VERSION_ID",
          "created_at": "2026-09-24T10:00:00.000Z",
          "expires_at": "2026-09-25T10:00:00.000Z",
          "validation": { "passed": 8, "total": 8 },
          "source_job": "YOUR_LEARNING_JOB_ID"
        }
      ]
    }
  ]
}
```

The example uses illustrative timestamps and validation counts. Retention deadlines come from confirmed checkpoint state and should not be inferred from this example's one-day interval.

## Subject fields [#subject-fields]

`tenant_id` identifies the customer, or is `null` for the project namespace. `id` is the subject name you supplied, not an internal database ID. `active_version` is a version ID or `null` when the subject uses base. `revision` increments when activation changes the route. It is not a monotonically increasing adapter version number and does not tell you how many candidates were trained.

`versions` contains up to the 50 most recent saved versions for that subject, newest first. A saved version can be inactive because activation was disabled, another update won a race, or a different version was activated later.

## Version fields [#version-fields]

The version's `id` is the public value accepted by the activation endpoint. `created_at` and `expires_at` are ISO UTC timestamps. `validation.passed` and `validation.total` are integer counts; unlike a job's validation result, `passed` here is not a boolean.

`source_job` links the version to the internalization that created it. Read that job to inspect the original activation outcome and settled charge. The adapter's parent is recorded as `adapter_version` on the source job.

## Bounded listing [#bounded-listing]

The endpoint returns up to 100 subjects and 50 recent versions per subject. Select a namespace with `X-Internalize-Tenant`; a restricted key selects its bound tenant automatically. Without either, only the separate project namespace is listed. It has no pagination or additional filtering parameters. Do not treat an absent older version as proof of deletion or expiration; an active version can fall outside the recent-version window after many other candidates are saved.

Keep the version IDs and source job IDs important to your own release history. If you outgrow the current listing bounds, contact support rather than scraping private console internals.

## Activation workflow [#activation-workflow]

Read `active_version`, select the candidate to deploy, and send both to [Activate an adapter](/docs/api-reference/activation). The expected version guards against a concurrent release between the read and write. Reading the subject does not lock it.

There is no separate public create, rename, merge, or delete subject endpoint. The first admitted model operation creates a missing subject. See [Projects and subjects](/docs/concepts/subjects) before choosing the identity scheme for your application.


---

# Credits and pricing

Source: https://convergingthought.com/docs/billing

> Prepaid usage, learning budgets, memory hosting, and settlement.



Internalize uses one prepaid project balance for inference, learning, and memory hosting. The minimum purchase is **$10*&#x2A;; the suggested purchase is **$25**. There is no free trial, subscription, automatic top-up, or automatic overage invoice. Prices are in US dollars; checkout shows applicable taxes.

## Rates [#rates]

| Usage                       |                       Rate |
| --------------------------- | -------------------------: |
| Input                       |       $12 / million tokens |
| Verified cached input       |     $2.50 / million tokens |
| Output, including reasoning |       $30 / million tokens |
| Training                    |       $36 / million tokens |
| Retained memory             | $0.30 / GB-month, prorated |
| Fee per internalize call    |                       None |

These are the `2026-09-27` rates for GLM 5.3. Each request records the price version at admission. A later rate change cannot reprice an already admitted job. Requests admitted under the earlier price version retain that version for settlement.

## What learning uses [#what-learning-uses]

An internalization prepares a curriculum, generates teacher answers, scores them, trains an adapter, and validates the result. Sampling uses the input and output rates above. Training uses the training rate for every processed exposure, including repeated epochs. Cached input receives the lower rate only when the provider reports a cache hit. The source passage's token count alone is not the bill.

There is no completion surcharge. A candidate that fails validation still consumed compute; that completed compute is charged. A saved candidate with activation disabled or conflicted is billed for its compute and retained weights. Internal infrastructure failures are absorbed by us. An ambiguous provider outcome stays in reconciliation until usage can be established, rather than being charged from a guess.

## Set a learning budget [#set-a-learning-budget]

Every internalization requires `max_cost_microusd`. This is the maximum **total** charge for the call, including the first 24 hours of a successfully saved memory. One dollar is 1,000,000 micro-USD. The supported per-call limit is $1–$100.

```json
{
  "subject_id": "support-agent",
  "content": "Return labels are prepaid for all Northstar orders.",
  "max_cost_microusd": 5000000,
  "activate": true
}
```

This example authorizes at most $5. Admission reserves that amount against both the available balance and monthly project limit. It does not charge $5 immediately and does not guarantee the recipe will finish within $5. Work must stop before starting an operation whose conservative bound would exceed the budget. Known completed compute is charged if the limit is reached; unused credit is released after settlement.

The console shows the limit before submission. A limit is not a prediction of the final cost. Workload-specific cost estimates still need calibration against complete live runs before paid execution opens. Do not treat example budgets as a measured typical cost.

When integrating an agent, set this budget in trusted application code. Do not accept a spending limit, tenant, or memory identity from model-generated tool arguments. See [agent learning](/docs/integrations/learning).

## Memory hosting [#memory-hosting]

Both serving weights and training state occupy storage. The initial reservation allows up to 32 GB for 24 hours, which is **at most $0.32**, within the call's limit. Settlement uses verified checkpoint sizes and retention periods. Storage from a rejected candidate is not charged to the customer.

A retained pair around 31.7 GB costs about $0.32 per day, or $9.51 per 30-day month. This is an illustrative size from a research run, not a guaranteed memory size. Source text can be short while its adapter is large.

Initial retention is 24 hours. Automatic paid renewal is not enabled. Check the version's `expires_at`; after expiration, inference fails explicitly rather than silently returning the base model. Continuous hosting requires an explicit funded renewal implementation before it can be offered.

## Exact amounts [#exact-amounts]

All posted money uses integer micro-USD. For one request:

```text
compute = uncached input × 12
        + verified cached input × 2.5
        + output × 30
        + training × 36
```

The total compute charge is rounded up once to a whole micro-USD. Cached tokens are a subset of input, not an additional charge. Storage uses decimal GB and a 30-day month, prorated by verified retention time; its aggregate is also rounded up once. Integer arithmetic is used for large byte-duration products.

For example, 1,000 uncached input tokens and 200 output tokens cost $0.018. A learning workload with 200,000 input, 20,000 output, and 60,000 training tokens costs $5.16 before hosting. These are arithmetic examples, not predictions of a typical call.

## Reservations and settlement [#reservations-and-settlement]

Inference reserves a conservative UTF-8 input bound, rendering overhead, and the maximum output allowance at the recorded rate. Learning reserves its explicit budget. Available balance is posted balance minus open reservations. Concurrent requests cannot spend the same credit.

A known terminal result settles once, including duplicate completion events. Read `billing.settled`, `billing.pricing_version`, `billing.compute_microusd`, and `billing.storage_microusd` on the job. Charges exceeding the reservation or incomplete usage remain in reconciliation and do not silently overdraw the project.

## Purchases and refunds [#purchases-and-refunds]

Only a verified Polar payment event adds net paid principal to the project. A checkout redirect never creates credits. Tax is not spendable credit. Duplicate payment events are idempotent; refunds remove credited principal and can freeze new spending when existing commitments exceed the remaining balance.

The hosted billing preview does not take a payment or add spendable credit. Paid model execution remains disabled while the native executor and payment setup are completed. See [availability](/docs/getting-started/availability) and [spending limits](/docs/billing/limits).


---

# Spending limits and reservations

Source: https://convergingthought.com/docs/billing/limits

> Understand the two admission checks: available credit and monthly budget capacity.



A model request needs both enough prepaid credit and enough room under the project's monthly limit. These checks happen before admission and include reservations for concurrent work.

## Available credit [#available-credit]

```text
available credit = posted balance − open reservations
```

Suppose the project has $10 in posted balance and $3 reserved for running requests. It has $7 available for new reservations. When a reserved request settles below its bound, the unused portion becomes available again.

The reservation is not an extra fee. If a request reserved $0.05 and ultimately cost $0.02, the final charge is $0.02 and $0.03 of capacity is released. The completed job still records that its original reservation was $0.05.

## Monthly capacity [#monthly-capacity]

Admission also checks that current-month settled spend, all open reservations, and the new reservation fit within the hard limit. The limit defaults to $100 for a new project and can be changed in [Settings](/internalize/app/settings).

A project with plenty of credit can still receive `spend_limit_exceeded`. Buying more credit does not raise the cap. Conversely, raising the cap does not buy credit. Resolve the specific failed condition rather than changing both values without inspecting them.

## Calendar boundaries [#calendar-boundaries]

Monthly spend uses calendar months in UTC. Settled usage is attributed when settlement occurs. Open reservations remain relevant across a month boundary; the calendar reset does not free money held by unfinished work.

The Usage page shows a recent 30-day view rather than exactly the current calendar month. Its total can differ from the amount used by the monthly-limit check when the period crosses two months.

## Pause new spending [#pause-new-spending]

Set the monthly limit to $0 to prevent new paid admissions. Existing jobs continue and can settle. Lowering the limit below already-spent or reserved amounts has the same practical effect for new work; it does not cancel jobs or rewrite the ledger.

Revoking a key is a credential action, not a project-wide budget pause. Other valid keys can still submit if budget allows. Use the spending limit when the intended control is to pause the whole project.

## Reduce avoidable reservations [#reduce-avoidable-reservations]

Choose a sensible maximum output size. A large output ceiling reserves more even if answers are usually short. Do not reduce it so far that reasoning consumes the allowance before the final answer can be produced.

Limit application concurrency when many requests compete for available balance. Serialize dependent learning updates for version correctness as well as budget predictability. Unresolved reconciliation jobs retain their reservations until the outcome is established; repeated submissions do not make that balance available.

For exact prices, see [Credits and pricing](/docs/billing). For an unexpectedly held amount, inspect [Activity](/internalize/app/requests) and follow [job recovery](/docs/troubleshooting/jobs).


---

# Purchases, receipts, and refunds

Source: https://convergingthought.com/docs/billing/payments

> Follow credit from a verified payment event into the project ledger.



The hosted console currently uses a billing preview. You can inspect the purchase flow, but it does not send a live payment or add spendable credit. A completed preview explicitly leaves the balance unchanged.

## Live purchase flow [#live-purchase-flow]

When live billing is enabled, open [Billing](/internalize/app/billing), select **Add credits**, and choose an amount. The console offers $10, $25, $50, and $100 shortcuts, with a custom amount field. The current purchase form accepts whole-dollar amounts from $10 to $1,000.

Polar handles checkout and payment details. Applicable tax is shown at checkout. Internalize credits the project's paid net principal; tax is not converted into inference credit.

Keep the selected project in mind. Credits belong to the project associated with the checkout, not to every project in the account. The current console does not expose self-service balance transfers between projects.

## Payment confirmation [#payment-confirmation]

Returning from checkout does not itself create a balance. A verified payment event updates the durable payment record and credit ledger. This prevents an unverified redirect or repeated page refresh from granting credit.

Duplicate events for the same paid order do not issue credit twice. If checkout succeeded but the balance has not updated, inspect the receipt and project, then allow the payment event to reconcile. Do not immediately buy again merely because the first browser redirect was interrupted.

## Receipts and credit activity [#receipts-and-credit-activity]

The Billing workspace shows the most recent 100 ledger entries, including purchases, settled usage, and refunds. References link an entry to its payment order or job identity. The display can round dollars for readability, while the ledger retains micro-USD precision.

**Receipts & payment details** opens the payment portal when live billing is configured and the project has payment history. It is unavailable in the current preview. Payment-card details are managed by Polar rather than stored in the console's project record.

## Refunds [#refunds]

A refund removes the credited principal associated with the refunded amount. It does not erase historical inference or learning usage. If the resulting balance no longer covers open reservations, the project can be frozen against new spending until the mismatch is resolved.

Refund handling is monotonic: a later duplicate paid event cannot undo a recorded refund and mint the credit again. Do not try to repair a mismatch by manually changing a displayed balance or repeatedly revisiting a checkout URL.

The console does not provide a self-service refund initiation control. Contact [team@convergingthought.com](mailto:team@convergingthought.com) with the authorized project and order reference for a billing question. Do not include card numbers, full payment credentials, API keys, or source content.

For a usage discrepancy, start from the job's settled charge and the corresponding ledger entry. See [Credits and pricing](/docs/billing) and [Usage](/docs/product/usage) to distinguish a reservation, a charge, and a rounded summary.


---

# Data handling

Source: https://convergingthought.com/docs/concepts/data-handling

> Understand the source, result, checkpoint, and telemetry boundaries.



The learning source, inference messages, generated answers, and adapter checkpoints are different forms of data with different purposes. Keeping the source out of later prompts does not mean the source was never processed or that no execution records exist.

## During a request [#during-a-request]

The control plane stores the payload needed to dispatch an admitted job. The private worker receives that payload to run training or inference. It also records operation receipts so an interrupted paid operation can be investigated without blindly repeating it.

Internalization uses the source to build grounded examples and to guide the teacher. Later inference uses the submitted messages and the selected adapter. The service does not append the learning source to those messages.

The worker's provider processes data necessary to perform the requested computation. These docs do not claim zero retention, a particular data-residency region, or a contractual exclusion from all provider processing. Confirm any required production data terms before submitting regulated or contractually restricted material.

## After a known terminal outcome [#after-a-known-terminal-outcome]

The control-plane job payload is cleared on settlement. Worker payloads are cleared on known terminal completion. Final results and operational metadata remain in authenticated job records so the application can retrieve the answer and inspect usage.

An ambiguous job can require retained execution data for reconciliation. Clearing a browser form does not delete that durable state. The source's learned effects can also remain in a retained adapter even after the original execution payload is removed.

Checkpoint storage is private. Public responses expose version IDs and retention deadlines, not provider checkpoint paths. Versions are scoped to a project, optional tenant, and subject; routing does not borrow another subject's weights.

## Analytics and logs [#analytics-and-logs]

Product analytics record navigation, interactions, errors, and operational metadata. They exclude full API keys, passwords, learning passages, inference prompts, and completions. Session replay masks inputs and blocks designated model-content surfaces. Raw reasoning is not exposed as a returned transcript.

Apply the same separation in your own observability. Log job IDs, error codes, timings, adapter versions, and integer charges. Do not log authorization headers or request bodies merely to diagnose an authentication problem. Use access-controlled storage if your application must retain evaluation outputs.

## Corrections and deletion [#corrections-and-deletion]

Learning a correction is not a verified deletion operation. Activating an older adapter changes routing but does not erase newer retained versions, execution records, or historical charges. Creating a fresh subject starts with clean base weights but does not delete the old subject.

There is no public self-service subject or adapter deletion endpoint in the current API. Contact [team@convergingthought.com](mailto:team@convergingthought.com) for data-access or deletion requests, and identify the authorized project without sending secrets or raw content in the first message.

Keep a canonical source in your own governed storage. An adapter is not a document archive, and its retention deadline is not a promise to retain the original passage forever. See [Privacy](/docs/privacy) and [safe diagnostics](/docs/troubleshooting/diagnostics) for related handling rules.


---

# How learning works

Source: https://convergingthought.com/docs/concepts/how-it-works

> From source knowledge to a validated adapter that answers without the source in context.



Internalize uses context distillation to train an adapter for GLM 5.3. A teacher sees a source passage and demonstrates how to answer questions about it. A student is trained on those questions without seeing the source in its prompt. The resulting adapter carries the learned behavior into later inference.

## The learning path [#the-learning-path]

```text
Source knowledge
      │
      ▼
Grounded questions + held-out checks
      │
      ▼
Teacher: source + question → answer distribution
      │
      ▼
Student: question only → adapter weight updates
      │
      ▼
Validate without source → save candidate → activate
```

The diagram describes the implemented worker pipeline. It is not a live execution trace or a claim about an example's measured accuracy. Hosted execution availability is tracked [separately](/docs/getting-started/availability).

## 1. Build a grounded curriculum [#1-build-a-grounded-curriculum]

The worker compiles training questions and held-out questions from the submitted passage. Each generated example includes an expected answer and a supporting excerpt. The compiler checks that evidence is an exact substring of the source and that questions are not duplicated.

Questions are intended to cover recall, paraphrases, applications, and combinations of supplied facts. A passage with contradictions or missing definitions can make this stage fail. The service does not fetch a linked website or infer missing pages on your behalf: the `content` string is the learning source.

## 2. Learn from the teacher [#2-learn-from-the-teacher]

For each training question, the teacher receives the source and question. Its answer is checked against the grounded example. The worker scores the answer distribution and trains the student with the question but without the source passage in the student prompt.

The current implementation uses a rank-16 LoRA adapter and a top-20 distribution distillation recipe. Those are implementation details, not customer-tunable request fields. The public API accepts knowledge and an activation choice; it does not accept optimizer settings, rank, or an arbitrary training dataset.

For a new subject, the adapter starts from base. For an existing subject, the worker loads the selected parent version's weights into a fresh training client. This preserves an explicit version lineage while avoiding shared optimizer state between subjects.

## 3. Save and validate [#3-save-and-validate]

The worker saves private inference and training checkpoints, verifies their ownership and retention, and evaluates held-out questions against the candidate without the source in context. A passing candidate can be registered as a version; a rejected candidate does not replace the active model.

The job exposes the validation decision and counts. The threshold and curriculum size belong to the deployed recipe, so read the returned totals rather than assuming a fixed number of checks. Generated validation is a useful gate, but your product should also have its own evaluation set.

## 4. Route subsequent inference [#4-route-subsequent-inference]

After activation, a new inference request for the same subject resolves to the adapter. The messages you submit are the inference context. Internalize does not retrieve or append the original learning passage to make the answer work.

An adapter is learned model state, not a lossless document store. It can generalize, misremember, or answer outside the source. Keep exact records in your own system when you need citations, authoritative wording, or current transactional data. Use [evaluation](/docs/guides/evaluation) to measure the behavior that matters to your application.

For the state transitions around this pipeline, read [Job lifecycle](/docs/concepts/jobs). For rollout and rollback, read [Versions and routing](/docs/concepts/versions-and-routing).


---

# Core concepts

Source: https://convergingthought.com/docs/concepts

> The objects and boundaries behind a persistent model memory.



Internalize gives an application a stable subject to learn into and ask through. The subject stays the same while its active adapter version changes. Each operation is a durable job, so execution can continue after the HTTP connection or browser closes.

## Objects [#objects]

| Object          | What it represents                                               | Lifetime                                         |
| --------------- | ---------------------------------------------------------------- | ------------------------------------------------ |
| Project         | Access, subjects, credits, and spending controls                 | Shared across the application's requests         |
| API key         | A scoped server credential for one project                       | Until expiration or revocation                   |
| Subject         | A stable knowledge identity within a project and optional tenant | Across inference and learning calls              |
| Adapter version | An immutable trained candidate with validation metadata          | While its checkpoints are retained               |
| Active version  | The subject's current routing choice                             | Until a later activation                         |
| Job             | One admitted inference or internalization                        | From admission through settlement and inspection |

An adapter version is not an API key, a conversation, or a project. Keeping these identities distinct makes failures easier to diagnose. For example, using a different key for the same project does not create a fresh subject; using a new subject ID does.

## One passage, two contexts [#one-passage-two-contexts]

During learning, the teacher receives the source passage. Training transfers the resulting behavior into adapter weights. During later inference, the model receives your question and messages, with the active adapter loaded. Internalize does not add the source passage back to those messages.

This is the meaning of “knowledge without source context.” It does not mean inference has an empty prompt, that earlier chats are automatically remembered, or that every possible question will be answered correctly.

## State you can inspect [#state-you-can-inspect]

Every admitted job records the subject and the adapter selected at admission. It also records status, phase, usage, reservation, settlement, and the final result or error. Internalization results expose the candidate version, validation totals, and whether activation happened.

The [subject listing](/docs/api-reference/subjects) exposes the current active version, revision number, and recent retained versions. The revision changes on activation; it is not a count of successful learning jobs.

## Read next [#read-next]

Read [Subjects](/docs/concepts/subjects) to design knowledge boundaries, [How learning works](/docs/concepts/how-it-works) for the training sequence, and [Versions and routing](/docs/concepts/versions-and-routing) for deployment behavior. [Job lifecycle](/docs/concepts/jobs) explains asynchronous execution, while [Data handling](/docs/concepts/data-handling) describes what is retained for execution and recovery.


---

# Job lifecycle

Source: https://convergingthought.com/docs/concepts/jobs

> Follow one operation from admission and credit reservation through execution and settlement.



Inference and internalization are asynchronous jobs. The submission response is a receipt for durable work, not the final result. Your application can disconnect after saving the job ID and resume observation later.

## Admission [#admission]

Before creating a job, the platform authenticates the key, checks its scope, validates the body, and looks for an existing operation with the same idempotency key. For new work it resolves the subject's active adapter and verifies that available credits and the monthly limit cover the reservation.

The job, its adapter snapshot, the idempotency claim, and its credit reservation are recorded together. Concurrent submissions cannot spend the same available credit. An invalid or unfunded request does not create an accepted job that your application should poll.

## Execution states [#execution-states]

```text
queued → running → succeeded     inference result
                 → ready         validated learning candidate
                 → rejected      learning gate did not pass
                 → failed        known terminal error
                 → reconciliation_required
```

Some failures occur before execution starts, so clients should not require every intermediate state to appear. Polling can also skip short-lived transitions. Treat the latest job record as authoritative.

`phase` provides additional diagnostic context, such as dispatch or a worker operation. It is not a progress percentage or a stable list of steps to build a rigid UI around. Use `status` for application decisions and show phase as supplementary detail.

## Completion and settlement [#completion-and-settlement]

A known terminal outcome settles the reservation once. Inference charges use measured input and output tokens. Internalizations use measured teacher, scoring, evaluation, and training tokens, plus verified initial hosting for a saved candidate. The price version is fixed at admission. Unused reserved credit becomes available again.

Read `billing.settled` alongside `status`. Zero usage while a job is running does not prove no work has happened. A reservation is not a final charge, and the original reserved amount remains in the completed job for audit purposes.

On success, inference returns `result.text`. Internalization returns candidate, validation, and activation metadata. A rejected candidate does not replace the active adapter. A ready candidate can be saved without becoming active, so applications must inspect `result.activated` before promising that future requests use it.

## Ambiguous outcomes [#ambiguous-outcomes]

`reconciliation_required` means the platform cannot safely establish the provider outcome or complete usage. Examples include a lost worker lease after paid work may have started, an unconfirmed dispatch, or incomplete usage evidence.

The reservation stays held. Do not convert this state to a normal failure and automatically submit a new operation. An operator must examine execution receipts and settle the known outcome without replaying paid work. The same job can later become terminal after reconciliation.

## Client timeouts [#client-timeouts]

A client deadline, polling timeout, closed browser, or aborted HTTP read does not cancel a job. Resume by reading the saved ID. If the submission response was lost, repeat admission with the same idempotency key and unchanged payload to recover the original receipt.

No public cancellation endpoint or customer webhook exists yet. Use bounded polling or a durable application task that periodically reads the job. See [Polling and recovery](/docs/sdk-reference/polling) for the client pattern and [Job reference](/docs/api-reference/jobs) for every response field.


---

# Projects and subjects

Source: https://convergingthought.com/docs/concepts/subjects

> Choose stable identities that match who should share learned knowledge.



A subject is a named model-memory identity inside a project and, optionally, a customer tenant. Every inference and internalization request includes `subject_id`. The platform uses that ID to find the active adapter or, for a new subject, the clean base model.

## Naming rules [#naming-rules]

Subject IDs are case-sensitive strings between 1 and 128 characters. The first character must be a letter or number. Remaining characters may include letters, numbers, underscores, dots, colons, and hyphens.

```text
support-policy
customer_42
workspace.docs
tenant:8f14:assistant
```

`Support` and `support` are different subjects. Spaces, slashes, and email-address punctuation such as `@` are not accepted. Prefer opaque application identifiers over names, email addresses, or private document titles, especially if your own logs record subject IDs.

## Match the knowledge boundary [#match-the-knowledge-boundary]

Use one subject when all callers should share the same learned knowledge. A public product manual could use one subject per product. Personalized memory generally needs a subject per user or assistant. Customer-specific procedures should not share a subject with other customers.

Use `tenant_id` for the authenticated customer and `subject_id` for the memory within that customer. The same subject name can exist independently in multiple tenants. Your server chooses the authorized tenant; never let a prompt or unchecked client input choose it. Keys can be restricted to one tenant. An unrestricted project key remains a trusted server credential that can select any tenant in the project. See [Serve multiple customers](/docs/guides/multitenancy).

Use separate projects when you also need separate keys, balances, or spending limits. Tenant-restricted keys are supported; balances and spending limits remain project-wide. There are no per-subject credits or per-subject API key scopes.

## Creation and continuity [#creation-and-continuity]

The first successfully admitted operation creates a missing subject. No prior API call is necessary. A new subject starts from clean base; it never borrows an adapter from another subject or project.

Later internalizations begin from the subject's active version at admission. They create separate candidates rather than editing an existing version in place. If the subject has a saved candidate that was never activated, the next learning job still starts from the active version, not that unactivated candidate.

Inference requests do not automatically become memory. If an interaction contains something your application wants retained, select and authorize that knowledge, then submit a distinct internalization job. Your application remains responsible for deciding what deserves to become durable knowledge.

## Avoid accidental resets and accidental sharing [#avoid-accidental-resets-and-accidental-sharing]

Keep the user-to-subject mapping in your application database. Generating a fresh subject ID on every request discards continuity. Reusing one global ID for unrelated users creates shared learned state. Neither behavior is a routing bug; both follow directly from the IDs submitted.

For a clean comparison, deliberately create a new subject ID and ask it without teaching the source. For a release, preserve the production subject and manage its versions. The current API does not expose subject rename, copy, merge, or delete operations. A new ID is an independent subject, not a copy of the old one.

See [Agent memory](/docs/guides/agent-memory) for a server-side mapping pattern and [Versions and routing](/docs/concepts/versions-and-routing) for how repeated updates interact.


---

# Validation and quality

Source: https://convergingthought.com/docs/concepts/validation

> Read the candidate's checks correctly and separate learning success from product readiness.



Internalization has two distinct outcomes: whether a candidate passed its learning checks, and whether that candidate became active. A job can pass validation without activation. It can also finish execution with a rejection, leaving the existing subject unchanged.

## Returned validation [#returned-validation]

An internalization result may contain:

```json
{
  "validation": {
    "passed": true,
    "passed_count": 7,
    "total_count": 8
  }
}
```

These illustrative values mean seven of eight checks passed for that candidate. They do not mean 87.5% accuracy across all future requests. The service chooses the validation threshold and check count through its deployed recipe; clients should inspect the response rather than hard-code a denominator.

The subject listing uses a compact form, `validation: { passed, total }`, where both fields are counts. That differs from the job result, where `passed` is a boolean and the counts have explicit `_count` names. See the [subject schema](/docs/api-reference/subjects).

## What the worker checks [#what-the-worker-checks]

The curriculum separates training questions from held-out questions. Source evidence must match the submitted content. The worker validates teacher answers before using them and evaluates candidate answers without including the source in the candidate prompt.

This detects some malformed learning examples and some failures to acquire the supplied facts. It does not establish that the model will preserve every prior behavior, refuse every unsupported claim, or correctly apply a policy under every possible wording.

The public API does not accept custom retention-check arrays or a validation threshold. If your application needs additional tests, run them through inference and retain their results in your own evaluation system.

## Interpret each outcome [#interpret-each-outcome]

| Outcome                   | Meaning                                         | Application action                                         |
| ------------------------- | ----------------------------------------------- | ---------------------------------------------------------- |
| `ready`, activated        | A saved candidate passed and became active      | Test important behavior using the same subject             |
| `ready`, not activated    | A candidate passed but is not the current route | Inspect manual activation or a concurrency conflict        |
| `rejected`                | The candidate did not pass the required gate    | Review the passage and failed behavior before new learning |
| `failed`                  | A known execution error stopped the operation   | Read the job error and settlement                          |
| `reconciliation_required` | Outcome or usage is not yet established         | Preserve the job and request investigation                 |

## Build an independent evaluation [#build-an-independent-evaluation]

Write questions before training. Include direct recall, paraphrases, multi-fact applications, exceptions, and facts the source does not contain. Keep the source out of inference messages. Record the exact adapter version on each evaluation job so a later activation cannot confuse the results.

For sequential updates, include questions from earlier knowledge as well as the new passage. Training from parent weights gives continuity, but it does not prove that every earlier fact survives. Test corrections explicitly: ask the old wording, the new wording, and a scenario where the difference matters.

Use the [evaluation guide](/docs/guides/evaluation) for a concrete test matrix. Validation results are a release input. Your application's acceptance criteria decide whether the resulting behavior is suitable for its users.


---

# Versions and routing

Source: https://convergingthought.com/docs/concepts/versions-and-routing

> Immutable candidates, explicit activation, and a stable subject for every new request.



A subject has at most one active adapter. Internalization creates a new candidate; activation chooses which candidate new requests use. The platform hosts checkpoints and resolves routing, so clients use subject and version IDs rather than storage paths or provider handles.

## Request snapshots [#request-snapshots]

The platform resolves the active version when it admits a model request. That selection is recorded in `adapter_version` on the job. Running requests keep the snapshot even if the subject changes while they are queued or executing.

For inference, this field identifies the weights used to answer. For internalization, it identifies the parent weights used to learn. The newly created version is returned separately in `result.candidate.version_id`.

By default inference follows the active subject route. Set `adapter_version` to a retained candidate to evaluate it before activation. A pinned version must belong to the same project, tenant, and subject; foreign or expired versions fail. Use `adapter_version: "base"` for a deliberate clean-base comparison.

## Automatic activation [#automatic-activation]

Internalization defaults to `activate: true`. A validated saved candidate activates only if the subject's active version still matches the version captured at admission. This comparison prevents an older training result from silently overwriting a newer release.

Consider two jobs admitted while version A is active:

| Event                    | Active version | Result                                         |
| ------------------------ | -------------- | ---------------------------------------------- |
| Job B and job C start    | A              | Both train from A                              |
| B finishes and activates | B              | B becomes active                               |
| C finishes later         | B              | C is saved, but automatic activation conflicts |

C can still be `ready`, with `activated: false` and `activation_conflict: true`. It is a valid candidate that did not become the current route. Its successful internalization fee still applies. “Latest” means the latest accepted activation, not simply the version with the newest creation time.

## Manual activation [#manual-activation]

Submit `activate: false` if a human or release process must decide. After the job is ready, inspect the candidate, read the subject's current `active_version`, and call:

```json
{
  "version_id": "CANDIDATE_VERSION",
  "expected_version_id": "CURRENT_VERSION"
}
```

Send this body to `POST /v1/adapters/activate` with the `adapters` scope. Use `null` for `expected_version_id` when the subject has no active adapter. The response returns the selected `version_id` and incremented `revision`.

On `409 activation_conflict`, read the subject again and decide whether the proposed activation still makes sense. Blindly replacing the expected value defeats the concurrency protection. The endpoint has no separate idempotency-key contract; after a network timeout, read state before repeating it.

## Rollback and retention [#rollback-and-retention]

You can activate a retained older version with the same endpoint. This changes future routing; it does not undo completed responses, charges, or other saved versions. There is no public operation to reset an existing subject to base. A new subject starts clean.

Each version exposes its confirmed `expires_at`. Background hosting renews retained checkpoints, but the recorded deadline matters. If an active adapter is missing or expired, the request fails explicitly. It never silently falls back to base or another subject.

A successful activation is not permanent archival storage. Watch retention metadata and maintain your canonical source outside the adapter. See [Deploying updates](/docs/guides/rollouts) and the [activation reference](/docs/api-reference/activation) for operational examples.


---

# Authentication

Source: https://convergingthought.com/docs/getting-started/authentication

> Use project-scoped server keys with explicit permissions and a controlled rotation path.



The public API authenticates each request with a project API key. Console sessions are separate: a browser cookie lets you use the console, but it is not a bearer credential for `/v1`.

```http
Authorization: Bearer YOUR_PROJECT_KEY
```

Keys belong to exactly one project. Optionally restrict a key to one customer in the console. Your server sends `tenant_id` or `X-Internalize-Tenant` for that customer’s memory namespace; a restricted key infers its tenant when omitted. See [Serve multiple customers](/docs/guides/multitenancy). You do not send a project ID in model requests; the key selects the project. If two projects each have a subject named `support`, their knowledge and spending remain separate.

## Create a key [#create-a-key]

Open [API keys](/internalize/app/keys) and choose **Create API key**. Enter a name that identifies the workload, select permissions, and select an expiry. The console offers 30 days, 90 days, one year, or no expiration. Ninety days is selected initially.

The full secret appears only immediately after creation. Copy it to the server environment or your secret manager before closing the dialog. The platform stores a hash for authentication and a short prefix for identification. The listing cannot reveal the full key later.

## Permissions [#permissions]

| Scope         | Allows                                        | Typical use                       |
| ------------- | --------------------------------------------- | --------------------------------- |
| `read`        | Read jobs and subjects in the selected tenant | Monitoring and version inspection |
| `inference`   | Submit inference and read its own jobs        | Serving user questions            |
| `internalize` | Submit learning and read its own jobs         | Knowledge updates                 |
| `adapters`    | Activate a retained adapter                   | Reviewed deployments and rollback |

The default key has only `inference`. Select **Inference + learning** to add `internalize`. **Management only** is hidden under Advanced and requires acknowledgement; it grants `read` and `adapters`, with no inference or learning scope. A submitting key can poll its own jobs without project-wide `read`. Other keys need `read` to inspect those jobs. A key with `internalize` can request automatic activation as part of learning; the `adapters` scope controls the separate manual activation endpoint. Do not grant learning permission to a service you intend to make inference-only.

`GET /v1/models` requires a valid key but no particular scope. It is a useful first authentication check that does not start model work:

```bash
curl --fail-with-body https://convergingthought.com/v1/models \
  -H "Authorization: Bearer $INTERNALIZE_API_KEY"
```

## Keep keys on the server [#keep-keys-on-the-server]

Call Internalize from your backend or worker. Requests carrying an `Origin` header are rejected with `server_keys_only`; browser-side access with a project key is intentionally unsupported. Do not solve this by embedding the key in a proxy URL or disabling browser safeguards.

In a web application, authenticate your own user first, resolve their authorized subject on the server, and make the Internalize request there. Return only the result and metadata the user is entitled to see. Subject IDs alone are not access tokens.

## Rotate or revoke [#rotate-or-revoke]

Create a replacement key with the required scopes, update the intended server, and verify a read request. Then revoke the old key in the console. Revocation prevents later authenticated requests; it does not cancel jobs already admitted or erase their charges.

There can be at most 50 non-revoked keys per project. Expired keys should also be revoked when no longer needed, because the cap counts non-revoked records. If a secret is lost, create a replacement; the original cannot be recovered. If a key may have leaked, revoke it and inspect recent [Activity](/internalize/app/requests) and [Settings activity](/internalize/app/settings).

For `401 unauthorized`, check the exact bearer value, expiry, revocation, and project. For `403 insufficient_scope`, use an appropriately scoped key. Never include the secret itself in a support report.


---

# Preview availability

Source: https://convergingthought.com/docs/getting-started/availability

> What you can use today, what is gated, and how to interpret the examples in these docs.



The hosted site currently exposes a production-deployed **console preview**. The application and its stored project state are real, but paid model execution and live payment collection are not enabled. These are separate capabilities; loading the console is not evidence that a training job can run.

## Current surfaces [#current-surfaces]

| Surface                              | Hosted behavior                                                    |
| ------------------------------------ | ------------------------------------------------------------------ |
| Marketing and documentation          | Public and readable without an account                             |
| Email-code sign-in and project setup | Clerk-backed verified accounts                                     |
| Project settings and API keys        | Backed by the platform database                                    |
| Request, adapter, and usage views    | Show the project's recorded state; no invented successful jobs     |
| Billing                              | Explicit preview; no payment taken and no spendable credits issued |
| Inference and internalization        | Admission disabled with `execution_unavailable`                    |
| Authentication delivery              | Managed by Clerk                                                   |
| Public TypeScript package            | Not published; the SDK is a private workspace package              |

## Running examples [#running-examples]

The examples document the implemented public API shape for execution-enabled projects. On the current hosted deployment, an authenticated model submission returns `503` with `error.code: "preview_only"`. Repeating it with another key or a larger purchase preview cannot enable execution.

You can still inspect the [OpenAPI schema](/openapi.json), build request validation, integrate the job state machine, and prepare your server-side credential handling. Keep application tests clearly separate from live model evidence. A local mocked response can test your UI, but it cannot demonstrate that a model learned anything.

Read-only operations authenticate normally. For example, a valid key can read the model catalogue or the subjects visible to its project and scopes. Empty history in a new preview project is expected.

## Billing when enabled [#billing-when-enabled]

The product uses prepaid credit with a **$10 minimum purchase**, no free trial balance, and no automatic top-up. The approved rates are $12/M input, $30/M output, $2.50/M verified cached input, $36/M training, and $0.30/GB-month retained memory. Learning has no flat fee and requires an explicit budget. Live checkout will use Polar and verified payment events to credit the project.

A successful purchase preview changes no balance. A production payment redirect will also not be sufficient evidence on its own: the verified payment event is authoritative. See [Payments](/docs/billing/payments).

## Supported model and API limits [#supported-model-and-api-limits]

GLM 5.3 is the only model in the current API catalogue. There is no customer-selectable model field or automatic fallback model. Other models are planned, but no availability date is promised here.

The API exposes full asynchronous results. Streaming, cancellation, user-configured webhooks, adapter export, subject deletion, and a public MCP endpoint are not exposed. Do not build an integration around private worker routes to approximate them. If a missing capability is essential, contact [team@convergingthought.com](mailto:team@convergingthought.com) with the intended workflow.


---

# Choose an interface

Source: https://convergingthought.com/docs/getting-started/interfaces

> Use the console for inspection, HTTP for integration, and the tool boundary for agent memory.



All interfaces describe the same objects: projects, subjects, jobs, and adapter versions. Choose the interface that fits the caller; keep job IDs and subject IDs stable when moving between them.

## Console [#console]

The [console](/internalize/app) is the place to create API keys, inspect requests, review adapters, use the playground, and manage project spending. It authenticates with your session, so you do not paste a project key into the playground.

Use it to understand a response or investigate a specific job. A request sent through the API appears in the same project history as a playground request. The console does not create a second set of adapters or a separate billing balance.

## HTTP API [#http-api]

The public API is the portable integration surface. It accepts JSON over HTTPS and returns asynchronous jobs for inference and internalization. You can use any server-side HTTP client capable of setting bearer and idempotency headers.

Choose HTTP when you need a language not covered by the workspace SDK, want to generate a client from [OpenAPI](/openapi.json), or prefer to own transport behavior. Read [API conventions](/docs/api-reference/conventions) before implementing retries or pagination.

`/v1/chat/completions` provides OpenAI-compatible text, function calls, and buffered SSE. Existing frameworks can connect with a base URL, key, and model ID. Native `/v1/inferences` and `/v1/internalizations` return immediate durable job receipts. See [Integrations](/docs/integrations) for setup and compatibility limits.

## TypeScript client [#typescript-client]

The repository includes a private workspace package, `@internalize/sdk`. It provides typed submission, job reads, explicit activation, and a polling helper. It does not automatically retry writes. The package is not currently published to a public registry, so an external application should use HTTP or code shared through an authorized repository checkout.

See [TypeScript](/docs/sdk-reference/typescript) for the exact constructor and method signatures. Do not add `/v1` to the client's `baseUrl`; it accepts the platform origin and supplies endpoint paths.

## Python [#python]

There is no public Python SDK at present. The [Python guide](/docs/sdk-reference/python) uses the standard library to demonstrate submission, error handling, and polling without inventing an installable package. The private inference worker is an implementation detail, not a client SDK or a customer endpoint.

## Agent tool [#agent-tool]

An agent can request `internalize` through a tool handler in your application. The handler authorizes the subject, submits the learning job, waits or schedules a later read, and reports the actual validation and activation result. The model does not hold your API key.

There is no hosted MCP server in the current release. Framework-specific tool registration stays in your application. Start with the [agent integration guide](/docs/integrations/agents) for a tool schema and a reliable execution loop.


---

# Project setup

Source: https://convergingthought.com/docs/getting-started/onboarding

> Establish the project, credentials, and spending boundary before making model calls.



A project owns its API keys, subjects, adapter versions, requests, credit ledger, and monthly spending limit. Start by deciding which application should share those resources. A separate project is appropriate when you need a separate balance or a separate set of credentials.

## Enter the console [#enter-the-console]

Open [the console](/internalize/app), enter your email, and verify the code sent by Clerk. The same flow creates a new account or signs you into an existing one. No password or separate signup form is needed.

Your verified account owns its projects. Signing out ends the browser session without deleting project state. Paid execution and payment collection have separate launch gates; see [availability](/docs/getting-started/availability).

## Name the project [#name-the-project]

Enter a short, recognizable name, such as `Support assistant`. The name can contain up to 80 characters and can be changed later. It is a label, not a public API identifier. Copy the immutable project ID from [Settings](/internalize/app/settings) when you need to correlate operational records.

New projects start with zero credits and a $100 monthly spending limit. The limit is a ceiling, not a balance or an automatic purchase. Accounts can create up to ten projects. Team invitations and role administration are not part of the current console.

## Complete the setup path [#complete-the-setup-path]

The Overview workspace leads through three tasks:

1. **Create an API key.** Name it after the application that will use it. Choose only the scopes needed for its work, and save the secret immediately.
2. **Add credits.** Live purchases start at $10. The current billing preview demonstrates the flow without taking payment or creating spendable credits.
3. **Make an API call.** Once execution is enabled and the project is funded, follow the quickstart and inspect the resulting job.

These steps represent real state. Visiting a page does not complete a model request, and returning from checkout does not itself prove a payment succeeded.

## Choose the first subject [#choose-the-first-subject]

The project is your access and spending boundary. The subject is the knowledge boundary inside it. Use a stable ID for an assistant, customer, or knowledge domain, for example `support-policy` or `customer_42`. A new subject begins with clean base weights.

You do not create subjects in a separate form. The first admitted inference or internalization request establishes the identity. For multi-customer applications, set `tenant_id` from your authenticated customer mapping and use `subject_id` for the memory name inside that tenant. Reusing the same pair within the same project returns to its existing knowledge state; changing the ID starts a different subject.

## Verify readiness [#verify-readiness]

Before live use, confirm that your credentials are server-side, the key has the intended inference or learning access, the project has enough available balance, and the spending limit permits the reservation. Then submit one small known passage and evaluate its result. See [Authentication](/docs/getting-started/authentication), [Spending limits](/docs/billing/limits), and [Make your first call](/docs/getting-started/quickstart).


---

# Make your first call

Source: https://convergingthought.com/docs/getting-started/quickstart

> Connect your model client, then optionally add learned knowledge.



This walkthrough uses a fictional returns policy so you can inspect the entire request. It requires an execution-enabled project with prepaid credits and a server-side API key. The current hosted preview does not execute these calls; check [availability](/docs/getting-started/availability) first.

## 1. Prepare the project [#1-prepare-the-project]

Open [Overview](/internalize/app), create your project, and visit [API keys](/internalize/app/keys). The default **Inference** key is enough for a first model call. Choose **Inference + learning** for the optional teach-and-ask walkthrough below. Keep version-management keys separate. Save the key in a server-side secret store; it is shown only once.

When live billing is enabled, purchase at least **$10** in [Billing](/internalize/app/billing). There is no trial balance or free model execution. A preview checkout does not fund a project.

```bash title="Server environment"
export INTERNALIZE_BASE_URL="https://convergingthought.com"
export INTERNALIZE_API_KEY="YOUR_PROJECT_KEY"
```

Use the origin above without `/v1` at the end. All examples add their own versioned path. The environment variable is a placeholder here; do not commit your real key to source control.

## 2. Make a normal model call [#2-make-a-normal-model-call]

Install the OpenAI client with `pip install openai`. No subject or adapter is required:

```python
import os
from openai import OpenAI
client = OpenAI(
    base_url="https://convergingthought.com/v1",
    api_key=os.environ["INTERNALIZE_API_KEY"],
    max_retries=0,
)
answer = client.chat.completions.create(
    model="glm-5.3",
    messages=[{"role": "user", "content": "Explain test-time training simply."}],
)
print(answer.choices[0].message.content)
```

Use [Integrations](/docs/integrations) for TypeScript, Vercel AI SDK, LangChain, LangGraph, and the Agents SDK. Keep your existing agent runtime; Internalize supplies the model and optional learning.

## 3. Optionally submit knowledge [#3-optionally-submit-knowledge]

Choose a stable subject ID. This example uses `support-demo`. The first admitted operation creates the subject if it does not exist; there is no separate create-subject endpoint.

```bash title="Internalize a policy"
curl --fail-with-body "$INTERNALIZE_BASE_URL/v1/internalizations" \
  -H "Authorization: Bearer $INTERNALIZE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: support-policy-001" \
  --data '{
    "subject_id": "support-demo",
    "content": "Northstar accepts returns within 30 days of delivery. Return labels are prepaid. Items marked final sale cannot be returned. An exchange uses the same 30-day window.",
    "max_cost_microusd": 5000000,
    "activate": true
  }'
```

A newly accepted request returns HTTP `202`:

```json
{ "id": "YOUR_JOB_ID", "status_url": "/v1/jobs/YOUR_JOB_ID" }
```

Save that job ID alongside your operation's idempotency key. Acceptance means the request is durably recorded and credits are reserved. It does not mean the knowledge has been learned yet.

## 4. Wait for learning and activation [#4-wait-for-learning-and-activation]

Read the returned status URL on the same origin:

```bash
curl --fail-with-body "$INTERNALIZE_BASE_URL/v1/jobs/YOUR_JOB_ID" \
  -H "Authorization: Bearer $INTERNALIZE_API_KEY"
```

While `queued` or `running`, poll with increasing delays. A suitable schedule starts around one second and grows to a maximum of 15 seconds. Training can take minutes or longer; no fixed completion time is promised.

Continue only when the job reports `status: "ready"`, `billing.settled: true`, and `result.activated: true`. The candidate version is in `result.candidate.version_id`. Save it so the first inference can be checked against the version you intended to use.

If the candidate is ready but `activation_conflict` is true, another update changed the subject while training. Inspect [versions and routing](/docs/concepts/versions-and-routing) before deciding which version should be active. A `rejected` or `failed` job is not successful learning. For `reconciliation_required`, preserve the job and [contact support](/docs/troubleshooting/jobs); do not submit a replacement paid operation.

## 5. Ask without the policy [#5-ask-without-the-policy]

Use the same subject. Notice that the inference request contains only a question, not the policy you submitted above.

```bash title="Inference with learned weights"
curl --fail-with-body "$INTERNALIZE_BASE_URL/v1/inferences" \
  -H "Authorization: Bearer $INTERNALIZE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: support-question-001" \
  --data '{
    "subject_id": "support-demo",
    "messages": [
      { "role": "user", "content": "Can I return a final-sale item after ten days?" }
    ],
    "max_output_tokens": 2048,
    "temperature": 0
  }'
```

Poll this new job. On `succeeded`, read `result.text` and check `adapter_version` against the learned version. The expected policy behavior is to identify the final-sale exception. That expectation is a test you should run, not a fabricated response from the service.

## 6. Inspect usage and try a second question [#6-inspect-usage-and-try-a-second-question]

Open [Activity](/internalize/app/requests) to inspect both jobs. Learning is billed for measured compute with no flat fee. Inference costs $12 per million input tokens and $30 per million output tokens, including reasoning. Memory hosting is $0.30/GB-month, prorated. Exact settled charges appear on each job.

Try a differently worded question, an exception, and a question the passage cannot answer. Evaluate the answer without putting the policy back into context. A useful follow-up is “Who pays for the return label?”; an unsupported question is “What is the warehouse street address?”

For an exact transport retry, reuse the original idempotency key and body. For an intentional new question, use a new key. Continue with the [TypeScript client](/docs/sdk-reference/typescript) or [Python HTTP guide](/docs/sdk-reference/python) to automate the loop.


---

# Build agent memory

Source: https://convergingthought.com/docs/guides/agent-memory

> Turn selected, authorized knowledge into durable subject state without storing credentials in the model.



An agent-memory integration needs two decisions: what knowledge should persist, and which subject is allowed to learn it. The model can propose an `internalize` tool call, but your server should own both authorization and execution.

## Resolve the subject on the server [#resolve-the-subject-on-the-server]

Maintain a mapping from your authenticated user or workspace to a subject ID. Derive it from trusted application state. If the model supplies a subject argument, verify it against that mapping instead of treating it as authority to access any subject in the project.

```text
Authenticated user
    → authorized workspace
    → server-owned subject mapping
    → scoped Internalize project key
```

A project key is not restricted to one subject. Separate customer projects when your application needs separate credentials or billing boundaries, and use separate subjects when learned knowledge should remain independent inside a project.

## Select durable knowledge [#select-durable-knowledge]

Good candidates include stable preferences the user asked to retain, approved product rules, or a corrected procedure. Transient search results, unverified claims, and instructions embedded in external content need review before they become memory.

Keep the original source and its provenance in your own governed store. An adapter is not a queryable memory database and does not return a list of all learned facts. The platform's job history tracks operations and versions, not a semantic inventory of everything the model may know.

## Use one tool invocation identity [#use-one-tool-invocation-identity]

Bind the runtime's durable tool-invocation ID to the idempotency key. The key must satisfy the API's 8–128 character format; hash or map framework IDs that contain unsupported characters. Persist that mapping before submission.

If the runtime retries the tool after a timeout, recover the same job. If the user intentionally requests a different memory, create a new invocation and operation key. Do not derive the key only from the subject, because that would collide with every later update to it.

## Report the real outcome [#report-the-real-outcome]

Return a small result containing job ID, status, candidate version, validation, and activation. Tell the agent that admission is pending work, not completed memory. A ready candidate with an activation conflict is a release decision, not a successful change to the active subject.

After confirmed activation, continue inference with the same subject and omit the learned source. Your application still supplies the new question and relevant conversation messages. Past chats do not become persistent knowledge automatically.

## Control updates and cost [#control-updates-and-cost]

Queue dependent updates per subject so each one starts from the intended parent. Deduplicate repeated memory proposals in your own application before creating a new paid intent. Use project spend limits as a backstop, not as the agent's only memory policy.

Every internalization has an application-set budget and uses metered compute without a flat fee. Evaluation inference is separately billed. If an agent proposes many tiny updates, consider collecting a coherent approved passage before learning it, while staying within the content limit and preserving provenance.

For a schema and handler example, see [Agent tool integration](/docs/integrations/agents). For correction, regression, and unsupported-answer tests, see [Evaluation](/docs/guides/evaluation).


---

# Evaluate learned knowledge

Source: https://convergingthought.com/docs/guides/evaluation

> Test recall, applications, exceptions, and retention without leaking the source into the prompt.



Measure the behavior your application needs, using questions the model did not receive as a source passage during inference. The built-in validation gate helps reject unsuccessful candidates, but it is not a replacement for your own acceptance criteria.

## Define tests before learning [#define-tests-before-learning]

For the fictional Northstar returns policy, a small evaluation could include:

| Category     | Question                                                     | Expected behavior                                               |
| ------------ | ------------------------------------------------------------ | --------------------------------------------------------------- |
| Recall       | How long is the return window?                               | State 30 days from delivery                                     |
| Paraphrase   | My order arrived three weeks ago; can I start a return?      | Apply the window rather than quote unrelated rules              |
| Exception    | Can I return a final-sale item after ten days?               | Recognize the final-sale exception                              |
| Combination  | Is an exchange after 35 days covered by the standard window? | Combine exchange and timing rules                               |
| Missing fact | What is the returns warehouse street address?                | Avoid inventing an address absent from the source               |
| Scope        | Does this policy cover an order delivered outside the US?    | Preserve the stated scope rather than assume worldwide coverage |

These are evaluation expectations, not observed results or a claimed benchmark. Record what the actual model says when execution is enabled.

## Keep the test clean [#keep-the-test-clean]

Send only the question and neutral instructions needed for the task. Do not include the source passage, expected answer, or a leading hint that gives away the tested fact. Otherwise you may be measuring in-context reading rather than adapter learning.

Keep temperature and output limits consistent across comparisons. Use enough output allowance for the model to produce a final answer, remembering that the ceiling also includes reasoning. A truncated answer should be distinguished from an incorrect completed answer.

## Record the resolved version [#record-the-resolved-version]

For each case, save the test identity, subject, inference job ID, `adapter_version`, final status, visible answer, and your evaluation judgment. Store answer content in restricted evaluation storage, not general analytics.

If an activation occurs during the run, jobs admitted before and after it can use different versions. Group results by the recorded version rather than by the time you happened to open the dashboard.

## Compare before and after [#compare-before-and-after]

Evaluate a clean subject before learning, then evaluate the learned subject after activation using the same questions. A correct base-model answer to a public fact does not demonstrate that the update taught it. Private or fictional policy details make the change easier to isolate.

For an existing subject, include regression questions from earlier knowledge. Sequential training can change behavior beyond the new facts. A release should pass both the new-knowledge tests and the old behaviors your application still depends on.

## Understand the testing boundary [#understand-the-testing-boundary]

Public inference follows the active adapter. There is no direct candidate-version parameter, so an inactive production candidate cannot be queried through a hidden per-version route. You can rehearse a source on an isolated subject, but that creates a separately trained artifact, not a copy of the production candidate.

For strict releases, gate user traffic, activate deliberately, evaluate the exact resolved version, and roll back to a retained prior version if needed. Keep that limitation visible in your rollout design.

## Judge outcomes deliberately [#judge-outcomes-deliberately]

Separate factual correctness, policy application, unsupported additions, and answer usability. Exact string matching works for narrow identifiers but can reject valid paraphrases. A model-based judge also needs review; it is not independent ground truth merely because it returns a score.

If an evaluation fails, retain the evidence, review the source and route, and decide whether to revise knowledge or restore an earlier version. Do not silently add the source to production prompts and still report the result as learned source-free behavior.


---

# Prepare knowledge

Source: https://convergingthought.com/docs/guides/knowledge

> Write a self-contained source that supports clear questions, applications, and exceptions.



The `content` field is the source the learning pipeline receives. Good input states the facts the subject should learn and gives enough context to interpret them. A URL, vague reference, or instruction to fetch another document is not a substitute for the actual passage.

## Start with one coherent topic [#start-with-one-coherent-topic]

Prefer a bounded policy, procedure, product definition, or selected memory over a mixture of unrelated documents. The public input accepts 20–16,000 string-length units. That cap is a validation boundary, not a recommendation to fill every request to its maximum.

This fictional passage includes scope, a rule, and exceptions:

```text
Northstar's standard return policy applies to online orders delivered in the US.
Customers may start a return within 30 days of delivery. Northstar provides a
prepaid return label. Items marked final sale cannot be returned. An exchange
uses the same 30-day window. Damaged items should be reported to support before
the customer ships anything back.
```

The wording supports both direct questions and applications. It also avoids implying a worldwide policy when only US orders were described.

## Make references explicit [#make-references-explicit]

Expand local abbreviations, identify the product or organization, and include units and time bases. “Returns are allowed for 30” is incomplete; “within 30 days of delivery” defines the window. “Use the process above” fails when the referenced process is absent from the submitted content.

If a fact depends on a condition, include both. Put exceptions close to the rule they modify. Distinguish current policy from historical background so the learner is not asked to infer which contradictory statement should win.

## Separate data from instructions [#separate-data-from-instructions]

Treat imported documents, chats, and tool results as source data. Select the knowledge your application is authorized to retain. Do not internalize an entire untrusted page simply because it contains instructions telling the agent to remember or obey something.

Keep runtime behavior instructions in your application's system messages when appropriate. Internalization is a knowledge update with persistent effects; it should not become an unreviewed route for changing authorization or revealing another user's data.

## Handle larger sources [#handle-larger-sources]

Divide a larger body of knowledge into coherent passages. Maintain a manifest in your own system recording the source revision, passage identity, subject, operation key, job ID, and resulting candidate. The public API does not currently accept arbitrary source metadata fields.

For updates to one subject, serialize learning when you want each new passage to build on the previous active version. If several jobs start from the same parent, they create competing candidates rather than automatically merging their knowledge. Wait for activation before admitting the next dependent update.

## Review a correction [#review-a-correction]

State the new rule, its scope, and what changed. Then test the old and new formulations. Training on a correction is not a guaranteed deletion of the previous fact, so keep canonical records and independent tests when exact policy changes matter.

If a passage fails validation, inspect whether it lacks evidence, combines too many topics, or contains contradictions. Improve the source deliberately and submit a new operation with a new key. Repeatedly submitting unchanged material until one run passes is not a quality strategy.

Continue with [Evaluation](/docs/guides/evaluation) and [Deploying updates](/docs/guides/rollouts). Keep the source out of evaluation prompts when measuring what the adapter itself learned.


---

# Serve multiple customers

Source: https://convergingthought.com/docs/guides/multitenancy

> Isolate each customer's learned weights with one tenant ID and a memory name.



Use `tenant_id` for the customer whose knowledge you are learning or using. Use `subject_id` for the memory within that customer. The complete identity is **project → tenant → memory**. There is no tenant-creation request, agent object, or additional deployment to manage.

```json
{
  "tenant_id": "customer_123",
  "subject_id": "support-agent",
  "content": "Northstar accepts returns within 30 days of delivery. Return labels are prepaid.",
  "max_cost_microusd": 5000000,
  "activate": true
}
```

Send that body to `POST /v1/internalizations` with your server key and a durable `Idempotency-Key`. Wait for a settled `ready` result with `result.activated: true`. Inference with the same tenant and subject then resolves that memory's active version. The learning passage does not become part of the question's context.

## Choose the boundary [#choose-the-boundary]

| Your application                                | `tenant_id`                | `subject_id`                    |
| ----------------------------------------------- | -------------------------- | ------------------------------- |
| One support bot per business                    | Your business account ID   | `support-agent`                 |
| A personal assistant per user                   | Your authenticated user ID | `assistant`                     |
| Several agents sharing one customer's knowledge | Your customer ID           | A shared memory name            |
| Separate memories inside one customer           | Your customer ID           | A distinct name for each memory |

Both IDs are case-sensitive, 1–128 characters, starting with a letter or number. Subsequent characters can include letters, numbers, dots, underscores, colons, and hyphens. Prefer opaque IDs over emails or other personal information.

Two tenants can both have a memory called `support-agent` without sharing learned state. A new tenant/memory pair starts from clean base. It does not inherit another tenant's knowledge, an unscoped memory of the same name, or a saved but inactive candidate.

## Bind identity outside model input [#bind-identity-outside-model-input]

Your server authenticates the end user and looks up their authorized tenant. Never use a tenant ID suggested by the model, a prompt, a URL parameter from an untrusted caller, or an unchecked request body as authorization.

An unrestricted project key is a trusted server credential: it can select any tenant permitted by its operation scopes. Namespace selection does not replace your application's authentication. Keep keys out of browsers and mobile clients.

For a dedicated customer deployment, create a key with **Restrict to a customer** in [API keys](/internalize/app/keys). The tenant restriction is immutable. Omitted tenant IDs resolve to that key's tenant; attempts to select another return `403 tenant_scope_mismatch`. Permissions still default to inference only. Enable learning separately, or use a separate management key when a service must change retained versions.

For many customers, use a shared server key and select the authorized tenant on each request. You do not need a key per customer; a project supports up to 50 non-revoked keys.

## OpenAI-compatible clients [#openai-compatible-clients]

Set the tenant and memory in request headers. This keeps the normal Chat Completions body unchanged and works with clients that do not expose custom body fields.

```ts
import OpenAI from "openai";

const model = new OpenAI({
  baseURL: "https://convergingthought.com/v1",
  apiKey: process.env.INTERNALIZE_API_KEY,
  maxRetries: 0,
});

// Resolve this from your verified session on the server.
const authorizedTenant = "customer_123";
const response = await model.chat.completions.create(
  {
    model: "glm-5.3",
    messages: [{ role: "user", content: "Who pays for a return label?" }],
  },
  {
    headers: {
      "X-Internalize-Tenant": authorizedTenant,
      "X-Internalize-Subject": "support-agent",
      "Idempotency-Key": "return-label-question-001",
    },
  },
);
```

Native and Chat Completions bodies also accept `tenant_id`. A body value and `X-Internalize-Tenant` must agree when both are present. Conflicts return `422 invalid_request`; no operation is admitted. Chat Completions without a subject still explicitly uses base weights, even if a tenant was supplied.

For Python, use `extra_headers={"X-Internalize-Tenant": authorized_tenant, "X-Internalize-Subject": "support-agent"}` on `create`. Framework clients can pass the same headers through their provider configuration. Use per-request options or an immutable per-customer client; do not mutate a shared client's defaults during concurrent requests.

## Bind the learning tool and polling together [#bind-the-learning-tool-and-polling-together]

The workspace SDK's `forTenant` returns an independent client. Learning, inference, polling, and activation on that client all carry the same tenant header.

```ts
import { InternalizeClient, createInternalizeTool } from "@internalize/sdk";

const shared = new InternalizeClient({
  baseUrl: "https://convergingthought.com",
  apiKey: process.env.INTERNALIZE_API_KEY!,
});
const customer = shared.forTenant("customer_123");
const tool = createInternalizeTool(customer, "support-agent", {
  maxCostMicrousd: 5_000_000,
});

// Only content is a model-supplied argument. Your runtime persists invocationId.
const result = await tool.execute(
  {
    content:
      "Northstar accepts returns within 30 days. Return labels are prepaid.",
  },
  "policy-update-001",
);
if (!result.activated) throw new Error(`Review learning job ${result.job_id}`);
```

A tenant-bound client cannot be rebound to a different tenant. The API key's server-enforced restriction still applies regardless of which SDK is used. The SDK is a private workspace package; see [TypeScript client](/docs/sdk-reference/typescript) for packaging and error behavior.

## Read and manage in the same namespace [#read-and-manage-in-the-same-namespace]

Repeat `X-Internalize-Tenant` on `GET /v1/jobs/{id}`, `GET /v1/jobs`, `GET /v1/subjects`, and `POST /v1/adapters/activate`. A tenant-restricted key supplies its own namespace when the header is omitted. A key without that restriction must explicitly select the tenant on reads, even when it knows the resource ID.

Job reads and lists return `tenant_id`; subject lists return it beside each memory name. A job or retained version from another tenant returns `not_found`. Pinning a version also requires the exact owning memory in the same tenant. A broad management key does not silently bypass the namespace selected on a request.

In the console, project owners can see all customer memories and activity. Memory rows show their customer ID and can be searched by either name or customer. Opening a memory in Playground preserves both identities. The sidebar's **Customer & version** section exposes the same namespace and version controls.

## Retries, billing, and existing applications [#retries-billing-and-existing-applications]

Idempotency keys are scoped to project and tenant. Two customers may use the same operation key independently. Within one tenant, retry with the same API key, body, and operation key; a changed payload or submitting key conflicts. A tenant-restricted key produces the same request identity whether its matching tenant is explicit or omitted.

With no tenant on an unrestricted key, calls use the original, separate project namespace. Existing memory and job IDs keep working. The console labels that namespace **Project memory**. It is not a shared fallback for customer namespaces. To move an application, explicitly learn or import its canonical knowledge under the new tenant; changing an ID does not copy weights.

All tenants share the project's credit balance, spending limit, and admission rate limit. Tenant IDs are not independent billing accounts. Use job usage records for your own customer accounting; use separate projects when you need separate balances. There is no cross-tenant memory merge, automatic inheritance, tenant deletion endpoint, or claim of dedicated per-tenant compute.


---

# Integrate a production application

Source: https://convergingthought.com/docs/guides/production

> Keep authorization, operation identity, budgets, and observability aligned across the whole request path.



Your backend should be the boundary between an end user or agent and Internalize. It holds the project key, chooses the authorized subject, records durable intents, and decides when a result can be used by the application.

## Establish the request path [#establish-the-request-path]

Authenticate the caller in your own application. Resolve their project and subject mapping from trusted state. Validate the proposed question or knowledge update, then create an operation record before calling Internalize.

Return an application operation ID if the work is still pending. A durable background task can read the Internalize job and update your record. This avoids tying a potentially long training job to one short-lived browser connection or serverless request.

## Separate capabilities [#separate-capabilities]

Use separate credentials for inference serving and learning or release automation when practical. An inference-only service should not be able to train arbitrary knowledge or manually activate a version. Submitting keys can poll their own jobs. Add `read` only for project-wide inspection.

Keep the key out of the model prompt and browser bundle. Do not let a tool argument select an unrestricted subject in a shared project. The model proposes an action; the server verifies that the action belongs to the authenticated caller's workspace.

## Bound work and spending [#bound-work-and-spending]

Choose an output ceiling appropriate for your answer format. Large ceilings increase the admission reservation even when actual output is usually small. Keep the project spending limit aligned with the application's intended budget, and observe available credit separately from the monthly cap.

Queue dependent internalizations per subject. Apply application-level deduplication to repeated memory proposals before creating new paid intents. A retry of the same intent keeps its key; a deliberate new update receives a new identity.

## Preserve operational evidence [#preserve-operational-evidence]

Record the job ID, HTTP request ID when available, status, error code, adapter version, timings, and final integer charge. Link a learning job to your source revision without putting source content into ordinary logs.

Keep metrics for admission failures, time to terminal outcome, rejected candidates, activation conflicts, unsettled reservations, and reconciliation cases. The public API currently offers polling rather than customer webhooks, so your monitoring task should read known jobs and back off appropriately.

## Recover safely [#recover-safely]

On a lost admission response, recover with the same body and key. On a failed poll, read the same job later. On reconciliation, preserve the reservation and request investigation. On an expired adapter, fail explicitly in your application rather than silently switching to an untrained subject and claiming the same knowledge is available.

A client timeout or user dismissal is not cancellation. Make that clear in your own UI and avoid a retry button that starts duplicate paid work.

## Verify the enabled deployment [#verify-the-enabled-deployment]

The current hosted environment is a console preview with billing and execution gated. Before live use, confirm that your project has a durable account, live payment setup, an actual paid balance, and enabled model execution. Then run the small end-to-end quickstart and inspect real jobs and charges.

Read [Availability](/docs/getting-started/availability), [Polling](/docs/sdk-reference/polling), and [Deploying updates](/docs/guides/rollouts). These docs describe the supported public interface; provider worker credentials and private service routes stay behind the platform boundary.


---

# Deploy knowledge updates

Source: https://convergingthought.com/docs/guides/rollouts

> Sequence learning, activation, evaluation, and rollback without overwriting another release.



Internalize manages adapter hosting and routing. Your release process decides which learned version should serve users and when. A stable subject lets applications keep one identity while that active version changes.

## Record the current state [#record-the-current-state]

Read the subject before starting a release. Save its active version, revision, the new source revision in your own system, and the idempotency key for the learning intent. Preserve the prior version ID as the rollback target while it remains retained.

Do not assume the newest-created candidate is active. A previous candidate may have been saved with activation disabled or may have lost a concurrency race. Use `active_version` from the subject state.

## Choose automatic or reviewed activation [#choose-automatic-or-reviewed-activation]

For a simple serialized memory workflow, use `activate: true` and wait for both `ready` and `activated`. Admit the next dependent learning job only after the intended parent is active.

For a reviewed release, use `activate: false`. Inspect the returned validation and confirm that a candidate exists. Then read current routing and activate using `expected_version_id`. The comparison protects against another release changing the subject while you were reviewing.

## Evaluate the exact release [#evaluate-the-exact-release]

The public API does not support inference against an inactive candidate or weighted traffic splitting between versions. An isolated subject can rehearse the source, but it does not validate the exact production artifact.

If your application requires an evaluation gate before exposing the new adapter to users, pause or gate user traffic in your application, activate the candidate, run the acceptance suite, and resume traffic only after it passes. Verify `adapter_version` on the evaluation jobs. Account for the fact that already-admitted requests keep their old snapshot.

## Handle concurrent updates [#handle-concurrent-updates]

Two learning jobs admitted from the same parent create separate candidates. They do not automatically merge. If one activates first, the other can finish ready with `activation_conflict: true`.

Decide which source should take precedence. If both updates are needed in sequence, you may need a deliberate new learning operation from the chosen active parent. Reusing an old idempotency key with revised source will return a conflict; a new intended update needs a new key and incurs its own successful-call fee.

## Roll back [#roll-back]

Read the current version and activate the retained previous candidate with that value as the expected version. Confirm the subject state after the write. New inference uses the restored route; queued or running work does not retroactively switch.

Rollback does not delete the unsuccessful release, erase completed answers, or refund prior charges. If the earlier adapter expired, the normal rollback path is unavailable. Inspect retention before relying on a version as a recovery option.

## Keep a release record [#keep-a-release-record]

Store source revision, subject, parent version, learning job, candidate version, validation totals, activation revision, evaluation evidence, and rollback target. These links make it possible to explain which knowledge produced an answer without exposing the source in routine logs.

The platform does not provide adapter export, alias creation, cross-subject cloning, or a public reset-to-base operation. Use the supported version and subject boundaries rather than building release scripts against private checkpoint paths.


---

# Internalize

Source: https://convergingthought.com/docs

> Learn knowledge into model weights. Keep the source out of subsequent prompts.



Internalize turns a passage of knowledge into a versioned adapter for GLM 5.3. Call `internalize` for a subject, wait for training and validation, then send questions to the same subject. Internalize hosts the adapter and routes new requests to its active version.

The source is used during learning. It is **not appended to later inference messages**. Your application still sends the question and any instructions or conversation it needs; the learned knowledge is carried by the adapter weights.

<Callout title="Hosted preview">
  The console is currently open for exploration. Billing is a preview and model
  execution is disabled. The guides below describe the implemented API contract
  for enabled projects. See [availability](/docs/getting-started/availability)
  before running examples.
</Callout>

## Start here [#start-here]

<Cards>
  <Card title="Make your first call" href="/docs/getting-started/quickstart" description="Internalize a policy, wait for activation, and ask without the source." />

  <Card title="Understand the system" href="/docs/concepts/how-it-works" description="Subjects, context distillation, validation, and managed weights." />

  <Card title="API reference" href="/docs/api-reference" description="Every endpoint, request field, response, limit, and error." />

  <Card title="Connect an agent" href="/docs/integrations/agents" description="A durable tool call with explicit learning and activation outcomes." />
</Cards>

## The three operations [#the-three-operations]

| Operation | Your application sends                            | Internalize returns                                        |
| --------- | ------------------------------------------------- | ---------------------------------------------------------- |
| Learn     | A subject and a self-contained knowledge passage  | A job that can produce a validated adapter candidate       |
| Activate  | A candidate and the version you expect to replace | A new active version, if the subject has not changed       |
| Ask       | The same subject and inference messages           | A job with the answer, resolved adapter, usage, and charge |

Learning activates automatically by default. Explicit activation is useful for reviewed releases and rollback. In both cases, already-running requests keep the version chosen when they were admitted.

## Find the right guide [#find-the-right-guide]

Use [Getting started](/docs/getting-started/onboarding) to prepare a project and credentials. The [Product guide](/docs/product/overview) explains each console workspace. [Concepts](/docs/concepts) covers the model and state boundaries; [Guides](/docs/guides/knowledge) shows how to structure knowledge, evaluate updates, and deploy them.

For an integration, choose [TypeScript](/docs/sdk-reference/typescript), [Python over HTTP](/docs/sdk-reference/python), or the [endpoint reference](/docs/api-reference). For operations, start with [billing](/docs/billing), [job recovery](/docs/troubleshooting/jobs), and [safe diagnostics](/docs/troubleshooting/diagnostics).

Validation is evidence about the checks run for one candidate. It is not a guarantee of perfect recall or general understanding. Test the questions, applications, and failure cases your product depends on before relying on a new version.

## Machine-readable documentation [#machine-readable-documentation]

Agents can discover these same guides through [/llms.txt](/llms.txt), read the complete set at [/llms-full.txt](/llms-full.txt), or fetch individual [Markdown pages](/docs/integrations/agent-docs). The [OpenAPI specification](/openapi.json) describes the public HTTP interface. These surfaces share the documentation source used by this site.


---

# Documentation for agents

Source: https://convergingthought.com/docs/integrations/agent-docs

> Discover the API and read focused Markdown pages before taking a paid action.



Internalize publishes machine-readable documentation alongside the browser docs. These routes are public and do not require an API key. They describe the interface; they do not grant permission to read a project or execute a model operation.

## Entry points [#entry-points]

| Route                                                                                  | Use                                                       |
| -------------------------------------------------------------------------------------- | --------------------------------------------------------- |
| [/llms.txt](/llms.txt)                                                                 | Discover sections and links to individual Markdown guides |
| [/llms-full.txt](/llms-full.txt)                                                       | Read the complete authored documentation in one response  |
| [/docs-markdown/getting-started/quickstart](/docs-markdown/getting-started/quickstart) | Read one page without the navigation interface            |
| [/openapi.json](/openapi.json)                                                         | Inspect machine-readable request and response schemas     |

Markdown paths follow the browser documentation slug. For example, `/docs/api-reference/inferences` has a corresponding `/docs-markdown/api-reference/inferences`. The docs index is available at `/docs-markdown`.

The index, full text, and per-page exports are generated from the same content source as this site. Each page includes its canonical URL so an agent can cite or revisit the human-readable reference.

## Suggested reading order [#suggested-reading-order]

Begin with availability and authentication. Read the conventions before constructing writes, then the specific endpoint and job schema. Use the polling guide before implementing a retry loop. Read billing if you are about to create paid operations, and activation semantics before changing a subject's route.

Fetch the smallest relevant pages when possible. The full export is useful for an initial system review, but repeatedly loading all guides is unnecessary for a narrow task such as checking a request-field limit.

## Keep authority separate [#keep-authority-separate]

Documentation explains what an operation does. Your user's instructions and your application's authorization determine whether you may do it. A retrieved source passage may contain instructions, but those instructions are knowledge input, not permission to select another subject, expose a credential, or spend more credit.

Never put an API key into a documentation URL or into a model-visible tool description. Keep credentials in the executing server. Record opaque job and operation IDs so the agent can inspect outcomes without receiving secrets.

## Inspect before retrying [#inspect-before-retrying]

Before repeating a model write, check whether an application intent or job already exists. Recover the original receipt with the same key and payload if needed. A timeout does not mean no execution occurred. Reconciliation means preserve the job and investigate rather than start a replacement.

There is no public hosted MCP server or installable agent skill endpoint in this release. Use the [tool integration guide](/docs/integrations/agents) to register a handler in your own runtime.


---

# OpenAI Agents SDK

Source: https://convergingthought.com/docs/integrations/agents-sdk

> Run an existing agent through the explicit Chat Completions model adapter.



The Agents SDK commonly defaults to the Responses API. Internalize supports Chat Completions, so select `OpenAIChatCompletionsModel` explicitly.

```bash
pip install openai-agents
```

```python
import asyncio
import os
from openai import AsyncOpenAI
from agents import Agent, Runner, OpenAIChatCompletionsModel, set_tracing_disabled

# Configure a separate tracing destination if your application needs one.
set_tracing_disabled(True)
client = AsyncOpenAI(
    base_url="https://convergingthought.com/v1",
    api_key=os.environ["INTERNALIZE_API_KEY"],
    max_retries=0,
)
agent = Agent(
    name="Assistant",
    model=OpenAIChatCompletionsModel(model="glm-5.3", openai_client=client),
)

async def main():
    result = await Runner.run(agent, "Hello!")
    print(result.final_output)

asyncio.run(main())
```

## Learned weights [#learned-weights]

Construct a client for an authorized subject with `default_headers={"X-Internalize-Subject": subject_id}`. The API key still selects the project. The header selects the learning identity inside that project, and must come from trusted application state.

An agent's instructions, current task, tools, and conversation history remain ordinary context. Internalized source material does not need to be pasted into that history after successful activation.

## Function tools [#function-tools]

Use ordinary function tools with `strict_mode=False`; strict constrained schemas are not supported by this endpoint. Validate arguments in the handler. The model may ask to execute a function, but your runtime retains responsibility for authorization and execution.

For `internalize`, take only `content` from the model and bind the subject on your server. Prefer a durable handler that records the API job ID and checks activation. If the operation outlives a runner invocation, resume that same job rather than recreate it.

## Compatibility limits [#compatibility-limits]

Responses API, hosted OpenAI tools, voice, images, and JSON-schema constrained decoding are not provided. Streaming uses buffered SSE, so an agent can consume the expected stream shape but does not receive token-level progress. Do not put an Internalize key into an OpenAI tracing client; tracing is a separate service and credential.

The [official models guide](https://openai.github.io/openai-agents-python/models/) explains the explicit Chat Completions adapter, and the [configuration guide](https://openai.github.io/openai-agents-python/config/) covers tracing configuration. Internalize's [API reference](/docs/api-reference/chat-completions) is authoritative for our supported request fields.


---

# Agent tool integration

Source: https://convergingthought.com/docs/integrations/agents

> Expose one learning tool with a trusted subject boundary and an inspectable asynchronous result.



An agent can call `internalize` through a tool registered in your application. Your handler translates that call into `POST /v1/internalizations`, persists the receipt, and returns the actual outcome. The Chat Completions API supports function-call messages, while your runtime executes the handlers. There is no hosted MCP endpoint. The [subject-bound learning guide](/docs/integrations/learning) is the recommended starting point.

## Tool definition [#tool-definition]

The workspace SDK exports this function-style tool definition:

```json
{
  "type": "function",
  "function": {
    "name": "internalize",
    "description": "Learn knowledge into this subject's model adapter. Submit once, wait for completion, and inspect activation before relying on it.",
    "parameters": {
      "type": "object",
      "properties": {
        "subject_id": { "type": "string" },
        "content": { "type": "string" }
      },
      "required": ["subject_id", "content"],
      "additionalProperties": false
    }
  }
}
```

Adapt the outer registration format to your agent runtime. The public endpoint still enforces content length and subject format. A valid tool JSON object is not itself authorization to train the named subject.

For a single-user assistant, you can omit subject selection from the model-facing tool and inject the authorized subject server-side. For a multi-workspace agent, check the selected subject against the caller's explicit access before submission.

## Workspace helper [#workspace-helper]

```ts
import { InternalizeClient } from "@internalize/sdk";
import { executeInternalize } from "@internalize/sdk/tool";

const client = new InternalizeClient({
  baseUrl: "https://convergingthought.com",
  apiKey: process.env.INTERNALIZE_API_KEY!,
});

const result = await executeInternalize(
  client,
  {
    subject_id: "authorized-workspace-42",
    content: "The approved knowledge this workspace has chosen to retain.",
  },
  "durable-tool-invocation-001",
  5_000_000,
);
```

The third argument is the stable invocation identity; the fourth is the application-set maximum charge in micro-USD. It must meet the idempotency-key format. Persist it in your runtime rather than generating a new one whenever a worker retries the same tool execution.

The helper requests automatic activation, waits, and returns `job_id`, `status`, `activated`, `version_id`, and `error`. It does not include validation totals in that compact result; read the job if the agent or application needs them. The helper can throw on HTTP errors, polling timeout, or reconciliation.

## Long-running tools [#long-running-tools]

If your runtime cannot keep a tool handler open, use `client.internalize` directly, save the job ID, and return a structured pending result. Schedule later observation and deliver completion through your application's normal workflow. Do not tell the agent that the memory is active merely because the POST returned `202`.

A useful result vocabulary distinguishes pending, learned-and-active, learned-but-needs-activation, rejected, failed, and needs-investigation. These application labels should map to the actual job fields rather than replace them in stored records.

## Continue inference [#continue-inference]

After successful activation, use the same subject for subsequent inference. The original passage can be omitted from the prompt. Continue supplying the new question and any conversation context needed for the current task.

The agent should inspect whether the intended version became active before relying on it. A conflict can leave a valid candidate saved while another version serves requests. Do not let an automatic loop overwrite the competing release without an application policy.

## Bound autonomous learning [#bound-autonomous-learning]

Select meaningful, authorized knowledge rather than internalizing every tool result or chat turn. Keep provenance outside the adapter, serialize dependent updates, and evaluate retained behavior after changes. Each successful saved call has a cost even when manual activation is deferred.

Use [Agent memory](/docs/guides/agent-memory) for the product pattern, [Polling](/docs/sdk-reference/polling) for durable execution, and [machine-readable docs](/docs/integrations/agent-docs) to let coding agents discover the same contract as a human developer.


---

# Vercel AI SDK

Source: https://convergingthought.com/docs/integrations/ai-sdk

> Use Internalize with generateText, streamText, and your own tools.



Use `@ai-sdk/openai-compatible` to target Chat Completions explicitly. Keep the provider and credentials on your server.

```bash
npm install ai @ai-sdk/openai-compatible
```

```ts
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
import { generateText } from "ai";

const internalize = createOpenAICompatible({
  name: "internalize",
  baseURL: "https://convergingthought.com/v1",
  apiKey: process.env.INTERNALIZE_API_KEY,
});

const result = await generateText({
  model: internalize("glm-5.3"),
  prompt: "Explain test-time training with an example.",
  maxRetries: 0,
});
console.log(result.text);
```

## Use an adapter [#use-an-adapter]

Resolve the subject in your authenticated server handler, then include `headers: { "X-Internalize-Subject": subject }` in `generateText` or `streamText`. Headers belong to that request; avoid mutating a shared provider for each customer.

An unseen subject starts from base. After learning activates a version, subsequent requests with the same subject route to it automatically. To compare against base, omit the subject entirely in a separate request.

## Tools [#tools]

The AI SDK converts ordinary function tools into the Chat Completions format. Define `inputSchema` and an `execute` handler using the SDK's `tool` helper. Your application owns the handler and its authorization. Validate tool arguments before using them for external actions.

For `internalize`, expose only a content argument. Bind the authorized subject in the server closure and store a durable idempotency key for that tool invocation. Do not generate a fresh key on each handler retry. Follow [Add learning](/docs/integrations/learning) for activation and long-running job semantics.

## Streaming [#streaming]

`streamText` can parse Internalize's SSE responses. Text and tool arguments are emitted after the complete model response passes validation, so the current stream is buffered. Show a waiting state until the first content arrives. Cancelling your UI stream does not cancel the durable model job.

## Supported surface [#supported-surface]

Use text prompts, text conversation messages, function tools, temperature, and output-token limits. Images, audio, embeddings, Responses-only features, and structured `json_schema` output are not available. Do not silently fall back to another model when an adapter request fails.

The protocol tests exercise `generateText`, `streamText`, and function-call parsing using the installed AI SDK against the same gateway serializers. They use a local transport and do not measure model quality or live provider availability.

See the [official compatible-provider guide](https://ai-sdk.dev/providers/openai-compatible-providers) for provider configuration and [the tool guide](https://ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling) for application-side execution.


---

# Connect your agent

Source: https://convergingthought.com/docs/integrations

> Use the model in your existing stack, then add learning when it is useful.



Internalize serves GLM 5.3 through an OpenAI-compatible Chat Completions endpoint. Start by changing your server's base URL, API key, and model name. You do not need to create an adapter or register an agent before making a normal inference call.

| Setting     | Value                              |
| ----------- | ---------------------------------- |
| Base URL    | `https://convergingthought.com/v1` |
| Model       | `glm-5.3`                          |
| Default key | Inference                          |
| API family  | Chat Completions                   |

The current hosted workspace is a preview. These examples target the execution-enabled contract; preview projects reject paid model work. See [availability](/docs/getting-started/availability).

## Choose your stack [#choose-your-stack]

* [OpenAI SDKs](/docs/integrations/openai): Python and TypeScript clients, text, tool calls, and buffered SSE.
* [Vercel AI SDK](/docs/integrations/ai-sdk): the OpenAI-compatible provider and tools in an existing server application.
* [LangChain and LangGraph](/docs/integrations/langchain): use the same chat model in your chain or graph.
* [OpenAI Agents SDK](/docs/integrations/agents-sdk): explicitly select the Chat Completions model adapter.
* [Add learning](/docs/integrations/learning): bind `internalize` to an authorized subject in your runtime.

Frameworks that accept a custom OpenAI-compatible endpoint can use the same configuration if their requests fit the [supported fields](/docs/api-reference/chat-completions). This is not a claim that every framework feature is supported: Responses, embeddings, images, audio, and JSON-schema constrained decoding are not available.

## Add a subject when you need learned weights [#add-a-subject-when-you-need-learned-weights]

A call without a subject always uses the base model. To use a customer's learned knowledge, send `subject_id` in the request body or set the `X-Internalize-Subject` header. Both select the same project-scoped identity. If both are supplied they must match.

The server resolves the subject's active adapter when admitting the request. No learning passage is appended to context. Keep sending the current question, tool results, and conversation history required for the task; learned weights do not replace conversation state.

Do not allow the model or an untrusted browser to choose another customer's subject. Resolve that mapping in your application after authenticating the caller. A project key can access subjects within its project; subject IDs are not separate authorization credentials.

## Decide who may learn [#decide-who-may-learn]

The default **Inference** key cannot train or manually activate adapters. **Inference + learning** adds permission to submit learning and request activation of its resulting candidate. **Management only** is a separate, acknowledged choice for release automation; it cannot run inference or train.

Your runtime owns tool execution. Merely declaring an `internalize` tool does not call the learning API, and the model never needs an API key in its prompt. Validate arguments, bind the authorized subject, then execute the call on your server.

## Recover without duplicate work [#recover-without-duplicate-work]

All inference paths use the same durable jobs and billing. Save `X-Internalize-Job-Id` and use a stable `Idempotency-Key` for operations that might be retried. Disable framework retries initially. If a request is pending or requires reconciliation, inspect its existing job rather than create a replacement.

A disconnected stream does not cancel model execution. Streaming currently uses valid SSE frames buffered until a final response passes validation. It is useful for client compatibility, but does not offer token-by-token latency yet.


---

# LangChain and LangGraph

Source: https://convergingthought.com/docs/integrations/langchain

> Keep your chain or graph and replace its chat-model connection.



`ChatOpenAI` can point to Internalize's compatible endpoint. Set `use_responses_api=False` so the integration uses Chat Completions.

```bash
pip install langchain-openai
```

```python
import os
from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    model="glm-5.3",
    base_url="https://convergingthought.com/v1",
    api_key=os.environ["INTERNALIZE_API_KEY"],
    use_responses_api=False,
    max_retries=0,
)
print(model.invoke("Hello!").content)
```

Use this model in your existing LangGraph agent or node. Internalize supplies inference and learning; your graph continues to own tools, conversation state, checkpoints, and application logic.

## Route to a subject [#route-to-a-subject]

For a model instance scoped to an authorized tenant, pass `default_headers={"X-Internalize-Subject": subject_id}` to `ChatOpenAI`. Resolve that ID from your application's authenticated identity, not a model-produced argument. Create a per-subject instance or use a request-scoped configuration; do not change headers on a shared instance during concurrent execution.

Without this header or a `subject_id` extra body field, inference explicitly uses base weights. A subject request resolves the active version when it is admitted. Running requests retain that immutable version.

## Tool messages [#tool-messages]

Use ordinary `bind_tools` function tools. Keep `AIMessage.tool_calls` and matching `ToolMessage` IDs in the conversation, and return all tool results before the next model call. The gateway rejects missing, duplicate, and unknown tool results.

If you expose learning, bind the subject in a server-side handler. Persist the graph run's tool operation identity as the idempotency key. A graph checkpoint can restore the same job receipt rather than submit a replacement call after a process restart.

## Bound the integration [#bound-the-integration]

Set retries to zero until your graph has explicit job recovery. Streams are buffered until final validation. Avoid strict structured-output helpers that request `json_schema`, unsupported multimodal content, or Responses-specific settings. An unknown field returns `422` rather than being ignored.

Learning is asynchronous and may exceed a web handler's lifetime. For long operations, store the job ID in graph state and resume observation from durable work. Check `activated` before assuming future calls can use the learned version.

See [LangChain's official OpenAI integration](https://docs.langchain.com/oss/python/integrations/chat/openai) and [polling and recovery](/docs/sdk-reference/polling).


---

# Add learning

Source: https://convergingthought.com/docs/integrations/learning

> Give an agent one tool that updates learned weights without exposing routing authority.



Begin with normal model calls. Add `internalize` when the agent encounters durable knowledge that should influence future answers. The tool submits a learning job; it does not inject the passage into later context.

## Choose an identity once [#choose-an-identity-once]

Use a stable subject for the customer, workspace, or assistant whose knowledge should evolve. Resolve that identity in your trusted server code. The model should supply knowledge, not decide whose weights to change.

Create an **Inference + learning** key for a server that needs both operations. It can poll its own jobs and request activation of the candidate it creates. It cannot manually switch arbitrary retained versions or list the entire project's activity. Use a separate management key for release automation.

## Register a small tool [#register-a-small-tool]

This definition works with ordinary function-calling runtimes:

```json
{
  "type": "function",
  "function": {
    "name": "internalize",
    "description": "Learn durable knowledge for this agent. Wait for the result and check activation before relying on it.",
    "parameters": {
      "type": "object",
      "properties": {
        "content": { "type": "string", "minLength": 20, "maxLength": 16000 }
      },
      "required": ["content"],
      "additionalProperties": false
    }
  }
}
```

Your handler validates the content and submits:

```json
{
  "subject_id": "AUTHORIZED_SUBJECT_FROM_YOUR_SERVER",
  "content": "THE_VALIDATED_TOOL_ARGUMENT",
  "max_cost_microusd": 5000000,
  "activate": true
}
```

Send it to `POST /v1/internalizations`, authenticated with the learning key. Supply a stable `Idempotency-Key` saved with the tool invocation. The model never receives that credential.

## Wait for the actual outcome [#wait-for-the-actual-outcome]

Persist the accepted job ID before waiting. Poll `/v1/jobs/{id}` with the same key. For short-lived agent handlers, return a structured pending result and have your runtime observe the job durably.

A `ready` result means a validated candidate was saved. Inspect `result.activated`: only successful activation changes routing. A concurrent update can leave a ready candidate inactive, with `activation_conflict: true`. Let an application policy or operator resolve that conflict; avoid a model loop that repeatedly overwrites versions.

Learning has no per-call fee. Set `max_cost_microusd` in your application; measured compute is charged even when validation rejects the candidate. Initial hosting is included within that limit. Failed or rejected learning does not represent learned knowledge. Reconciliation requires inspecting the existing operation, not repeating it.

## Workspace helper [#workspace-helper]

The private repository includes `@internalize/sdk`; it is not a published npm package. Authorized monorepo consumers can bind the subject with a helper:

```ts
import { InternalizeClient } from "@internalize/sdk";
import { createInternalizeTool } from "@internalize/sdk/tool";

const client = new InternalizeClient({
  baseUrl: "https://convergingthought.com",
  apiKey: process.env.INTERNALIZE_LEARNING_KEY!,
});
const learning = createInternalizeTool(client, authorizedSubject, {
  maxCostMicrousd: 5_000_000,
});
// Register learning.definition with your runtime.
// Save invocationId durably before the handler runs.
const result = await learning.execute({ content }, invocationId);
```

The helper rejects extra arguments, so model input cannot override its bound subject. It returns the actual job state and activation result. External applications can implement the same small handler using HTTP.

## Continue with a fresh question [#continue-with-a-fresh-question]

After activation, make a Chat Completions request with the same subject header or body field. Leave the source passage out. Keep any current conversation history your application needs.

The adapter stores learned behavior in weights; it is not a document store or a guarantee of perfect recall. Keep provenance and source material in your application and evaluate important behavior. You can inspect learned versions, validation counts, and activation in [Memory](/internalize/app/memory). There is no separate Memory area to keep synchronized.


---

# OpenAI SDKs

Source: https://convergingthought.com/docs/integrations/openai

> Connect Python or TypeScript with the Chat Completions API.



Use the standard OpenAI client on your server. Select `glm-5.3` and set the Internalize base URL, including `/v1`. An inference-only key is sufficient for these examples and for reading jobs it submitted.

## Python [#python]

```bash
pip install openai
```

```python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://convergingthought.com/v1",
    api_key=os.environ["INTERNALIZE_API_KEY"],
    max_retries=0,
)
response = client.chat.completions.create(
    model="glm-5.3",
    messages=[{"role": "user", "content": "Explain test-time training simply."}],
    max_completion_tokens=2048,
)
print(response.choices[0].message.content)
```

For learned weights, pass `extra_body={"subject_id": "authorized-customer-42"}` to `create`. Pin a retained version with `adapter_version` in the same object. Omit the version to resolve the active adapter at admission.

## TypeScript [#typescript]

```bash
npm install openai
```

```ts
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://convergingthought.com/v1",
  apiKey: process.env.INTERNALIZE_API_KEY,
  maxRetries: 0,
});
const response = await client.chat.completions.create({
  model: "glm-5.3",
  messages: [{ role: "user", content: "Hello!" }],
});
console.log(response.choices[0].message.content);
```

For a subject scoped to one trusted client, add `defaultHeaders: { "X-Internalize-Subject": authorizedSubject }` when constructing that client. For a shared client, pass `headers` in the per-request options. Never mutate shared default headers between concurrent customers.

## Multiple customers [#multiple-customers]

Pair `X-Internalize-Tenant: authorizedCustomerId` with `X-Internalize-Subject: memoryName`. In TypeScript, pass both in per-request `headers`; in Python use `extra_headers`. Body `tenant_id` and header scope must agree. Repeat the tenant on job reads and learning calls. See [Serve multiple customers](/docs/guides/multitenancy) for a complete flow and restricted keys.

## Function calls [#function-calls]

Declare function tools with `name`, `description`, and `parameters`. An assistant response can contain `tool_calls`. Execute authorized functions in your runtime and append one `role: "tool"` result for each call ID before continuing. Preserve the assistant's calls in history. The next request may end with the completed tool-result block.

`tool_choice` supports `auto`, `none`, `required`, or a named function. The service validates the resulting choice before delivering it. It does not execute your tools. Strict constrained tool schemas are not supported; omit `strict` or set it to false, and validate arguments in your own handler.

## Stream responses [#stream-responses]

Set `stream: true` and optionally `stream_options: { include_usage: true }`. Consume the result with the SDK's usual stream iterator. Internalize emits keepalive comments while the durable job runs, then validated content or function-call chunks and `[DONE]`.

This is buffered SSE, not live token generation. Raw reasoning is not returned. `completion_tokens` includes the reasoning that the model generated and billed, even though only the final answer is visible.

## Errors and retries [#errors-and-retries]

The SDK exposes normal HTTP errors for admission failures such as invalid keys, insufficient funds, and invalid input. A non-streaming post-admission failure or timeout returns `409` with `error.job_id` and `error.status_url`. A stream already started returns an error object in its data frames with those same recovery fields.

Keep `maxRetries: 0` / `max_retries=0` until your application persists operation identities. For a deliberate retry, supply the same `Idempotency-Key` and exact body. A new key means a new potential charge. See [Chat Completions](/docs/api-reference/chat-completions) and the [official request reference](https://developers.openai.com/api/reference/resources/chat/subresources/completions/methods/create).


---

# Privacy

Source: https://convergingthought.com/docs/privacy

> What the platform records.



Internalize processes account details, submitted model content, adapter state, payment references, and operational metadata to provide the service. Payment details are handled by Polar.

Product analytics include anonymous visits, navigation, errors, performance, and interaction events through PostHog. Authenticated activity is associated with an internal user and project ID. Session replay masks inputs and blocks model content and API secrets. Prompts, learning passages, model responses, passwords, and full API keys are excluded from analytics.

Job payloads are removed from the control plane after terminal settlement. The inference service retains the data required for execution and recovery under its configured retention policy. Ask us about data access or deletion at [team@convergingthought.com](mailto:team@convergingthought.com).


---

# Memory

Source: https://convergingthought.com/docs/product/adapters

> Manage learned knowledge, active model state, and retained memory versions.



[Memory](/internalize/app/memory) groups learned knowledge by memory ID. Each memory maps to a subject in the API. Open a memory to see its active state and version history; each version is encoded in an adapter.

Choose **Add knowledge** to internalize a passage directly from this page. **Try in playground** opens a fresh conversation with that memory selected. Learning creates a durable request that you can inspect in Activity.

This is learned model state, not a searchable document store. The page does not retain or reproduce the source passage, offer per-fact editing, or guarantee perfect recall.

## Memory list and details [#memory-list-and-details]

A subject with no active adapter uses the base model. A subject with an active retained version routes new requests to that version. The list shows status, loaded version count, last learned time, and active version. The detail view adds the memory ID, model, retention deadline, and routing behavior.

The listing is bounded to 100 subjects and 50 recent versions per subject. Treat the displayed count as the loaded recent history rather than an unlimited archival count. Keep important release IDs in your own application records.

## Version table [#version-table]

Each row shows a version identifier, validation counts, learned time, retention deadline, and a **Use version** control. The validation fraction describes that candidate's checks, not a universal model score. The retention deadline is confirmed checkpoint metadata, not a permanent-storage guarantee.

The active badge identifies the current route. Newest-created and active are different properties: an automatically conflicting candidate may be newer but inactive, and a rollback may intentionally activate an older version.

## Activate a version [#activate-a-version]

Choose **Use version** for a retained inactive version and review the confirmation. New requests will use it; running requests retain their original adapter. The operation includes the version the console expects to be active, preventing a concurrent route change from being silently overwritten.

If activation conflicts, refresh the subject state and review the other release. Do not repeatedly force the action without understanding which version should win. An expired candidate cannot be activated through the normal control.

## Roll back [#roll-back]

Select a retained older version and activate it using the same control. Rollback changes future routing. It does not refund learning, delete newer versions, or alter already-completed answers.

For subsequent learning, the rolled-back version becomes the parent of newly admitted internalizations. That matters if you were trying to retain knowledge from a newer release: choosing an older parent can intentionally omit those later updates.

## Investigate missing knowledge [#investigate-missing-knowledge]

First inspect the source job and its activation result. Then compare the subject ID and the inference job's recorded adapter version. An answer generated by an earlier snapshot is not evidence that a later candidate failed to learn.

If the route is correct, run an independent evaluation without the source in context. See [Versions and routing](/docs/concepts/versions-and-routing), [Deploying updates](/docs/guides/rollouts), and [Knowledge troubleshooting](/docs/troubleshooting/knowledge).


---

# Manage API keys

Source: https://convergingthought.com/docs/product/keys

> Create one credential per workload, inspect its identity, and revoke it when no longer needed.



Open [API keys](/internalize/app/keys) in the intended project. Keys are server credentials, so create them for the application or worker that will call Internalize rather than for individual browser tabs.

## Create [#create]

Select **Create API key**, enter a recognizable name, and choose permissions. The dialog defaults to **Inference** only, including access to its own request results. **Inference + learning** explicitly adds learning and activation of the resulting candidate. **Management only** is under Advanced and requires a separate acknowledgement; it can inspect project activity and activate retained versions but cannot run inference or train. Existing keys keep their current scopes. **Restrict to a customer** optionally binds the key to one `tenant_id` without changing its permissions. Leave it blank when your server serves multiple customers and selects an authorized tenant on each request. The restriction cannot be edited later; rotate the key to change it. See [Serve multiple customers](/docs/guides/multitenancy).

Select an expiry that fits your rotation process. The console offers 30 days, 90 days, one year, and no expiration. Avoid choosing an expiry shorter than your ability to update the deployment that will use the credential.

After creation, copy the full secret once and save it in the destination server's secret store. The success dialog is the only point at which the full key is available. Closing it does not save the secret into your application automatically.

## Identify and inspect [#identify-and-inspect]

Use the key's name, prefix, scope set, and expiry to identify it later. The prefix is a label, not a usable credential. Last-used information is updated by admitted model work; do not treat it as a comprehensive log of every successful read request.

The platform caps projects at 50 non-revoked keys. Expired but non-revoked keys still count toward that limit. Revoke retired records as part of routine credential cleanup.

## Rotate [#rotate]

Create the replacement first. Update your authorized server configuration and verify a read such as `/v1/models`, then verify the relevant scoped operation when execution is enabled. Revoke the old key after the application has moved over.

If a key is lost before it is saved, create a replacement and revoke the inaccessible one. The platform cannot reveal the original secret from its stored hash. If a key is exposed, revoke it promptly and inspect Activity for unfamiliar work.

## What revocation changes [#what-revocation-changes]

Revocation stops later requests authenticated with that credential. It does not cancel existing jobs, remove adapters, reset subjects, or reverse settled charges. A different valid key for the same project can continue reading jobs if it has `read` permission.

There is no public key-management REST endpoint in this release. Use the console rather than automating against private session routes. See [Authentication](/docs/getting-started/authentication) for the HTTP boundary and [Access troubleshooting](/docs/troubleshooting/access) for common key failures.


---

# Console overview

Source: https://convergingthought.com/docs/product/overview

> Find setup, credentials, requests, adapters, usage, and spending controls in one project workspace.



The [Internalize console](/internalize/app) is the operational view of your project. It uses the same durable state as the public API: a job submitted by your backend appears in Activity, and an adapter activated through the API appears in Memory.

## Workspaces [#workspaces]

| Workspace                                 | Use it for                                                         |
| ----------------------------------------- | ------------------------------------------------------------------ |
| [Overview](/internalize/app)              | Create a key, prepare credits, and make the first call             |
| [API keys](/internalize/app/keys)         | Create, inspect, and revoke server credentials                     |
| [Activity](/internalize/app/requests)     | Follow admitted operations and inspect their results               |
| [Memory](/internalize/app/memory)         | Inspect subjects, validation, versions, and retention              |
| [Playground](/internalize/app/playground) | Teach a passage and ask a question using the same subject          |
| [Usage](/internalize/app/usage)           | Review settled tokens, calls, and spend over the recent period     |
| [Billing](/internalize/app/billing)       | Review available credit, reservations, and credit activity         |
| [Settings](/internalize/app/settings)     | Change the project label and hard spending limit; inspect activity |

## Project context [#project-context]

Check the selected project before creating a key, funding a balance, or changing an adapter. Project keys automatically select their owning project in the public API; the browser's project selector does not change a key already installed in another service.

Project names are human-readable labels. Use the immutable project ID from Settings when matching a console issue to an application deployment. Changing the name does not rename subjects or invalidate credentials.

## Empty and pending states [#empty-and-pending-states]

A new project has no model jobs, no adapters, and no settled usage. These empty states are expected. The hosted preview does not populate fake successful calls or grant credits to make the dashboard look active.

A pending request can reduce available balance before any settled usage appears. Activity tracks the operation; Billing shows its reservation; Usage reflects it only once settlement is known. Those views answer different questions rather than refreshing at the same accounting moment.

## Move from inspection to integration [#move-from-inspection-to-integration]

Start in the playground to understand the sequence, then reproduce it with [the quickstart](/docs/getting-started/quickstart). Keep the same project and subject when you expect continuity. Use a separate subject when you intentionally want a clean base comparison.

The console is not an alternate source of model behavior or an implicit chat-memory system. It submits the same kinds of jobs and obeys the same funding, validation, and activation rules. See [availability](/docs/getting-started/availability) for the current hosted gates.


---

# Use the playground

Source: https://convergingthought.com/docs/product/playground

> Chat with the base model, add knowledge to memory, and inspect the version behind an answer.



The [Playground](/internalize/app/playground) has one conversation and a memory panel. It uses the same durable jobs, routing, and billing as the API. The current model is GLM 5.3; the hosted preview shows the interface but does not execute model work.

## Start with base [#start-with-base]

The initial mode is **Base model**. Enter a message in the composer, press Enter to send, or Shift+Enter for a newline. The conversation includes your earlier successful turns. Failed calls do not become invented assistant messages.

Base mode explicitly selects base weights. It does not inherit an adapter from a subject called `default`. Choose **New chat** to clear conversation history. That action does not delete learned adapters or cancel a durable request.

## Adjust the model [#adjust-the-model]

Open **Model settings** below the memory editor for model mode, temperature, maximum output tokens, and optional system instructions. On smaller screens, open **Memory panel** in the toolbar. Changing settings starts a fresh conversation to keep experiments from mixing different configurations.

Output limits include hidden reasoning tokens. A small limit can leave little room for a visible answer. Temperature ranges from zero to one; the default is zero. The playground does not display internal reasoning.

## Use learned weights [#use-learned-weights]

Open **Model settings**, choose **With memory**, then use **Memory settings** to enter a memory ID or select an existing one. This is the `subject_id` used by the API. The default version choice, **Auto · active version**, resolves the active adapter for each new call. Choose a retained version to pin an experiment. Missing, foreign, or expired versions fail rather than silently switching to base.

A new subject starts from base. The ID names an assistant or body of knowledge. In **Memory settings**, optionally choose a customer ID (`tenant_id`) to keep that customer’s memories separate. The same memory name can exist for multiple customers. These identifiers are not secrets; your application must authorize their selection.

## Internalize knowledge [#internalize-knowledge]

Enter 20–16,000 string-length units of knowledge in &#x2A;*What should it remember?**, then select **Internalize**. A memory ID is provided by default; open **Memory settings** only when you need another identity, customer, or version. **Use an example*&#x2A; fills a fictional fact and a question so you can try the flow with no setup; it does not make a paid call. The composer’s &#x2A;*+** opens the same panel, including on mobile.

Submitting requests a validated candidate and automatic activation. The learning status stays next to the knowledge: waiting for a worker, preparing data, updating weights, checking answers, and saving weights reflect the observed worker phase. There is no estimated percentage. **Run details** exposes the request in Activity, the resulting version, and reported validation counts. A rejected or inactive candidate never appears as ready to test. You can leave the page while the durable job continues. Set the call’s spending limit before submitting. Learning is billed for measured compute and initial hosting, with no flat fee; activation may require review if another version changed during training.

After successful activation, the primary action becomes **Test memory**. This clears conversation history and system instructions, pins the learned version, and sends only the new question. Each question in this test is independent, even when earlier results remain visible. The fact stays visible in the sidebar for your reference but is never appended to the inference request. Changing model settings exits this test mode and starts a new conversation. This lets you test learned behavior with paraphrases and application questions, rather than testing whether a model can repeat a passage already in context.

## Create memory from the Memory tab [#create-memory-from-the-memory-tab]

**Add knowledge** opens the same knowledge field and Tinker run details. The dialog remains open after submission so you can follow the job. Closing it leaves **View progress** on the Memory page while you remain there. Once the version is ready, **Test memory** opens the playground with the correct customer, memory ID, and exact version already selected for a fresh test. **Add more knowledge** preserves the memory identity for the next update.

## Inspect and integrate [#inspect-and-integrate]

Each response shows its resolved adapter, token count, settled charge, and **Inspect request** link. A pending job continues to reserve credits. Reconciliation means inspect the existing operation rather than click again to recreate it.

**View code** generates an OpenAI TypeScript request with your current messages and model settings. The snippet uses an environment variable for the API key and excludes learning-source content. The SDK should run on your server. For a clean test, View code contains only the pending question. See [Integrations](/docs/integrations) for other runtimes and a durable `internalize` tool.

Conversation history in the playground is temporary and is cleared when you leave. Completed job results remain in Activity. Keep canonical knowledge and evaluation records in your own application; the playground is a manual experiment surface, not a replacement for release evaluation.


---

# Activity

Source: https://convergingthought.com/docs/product/requests

> Follow status, routing, token usage, results, and charges for each admitted operation.



[Activity](/internalize/app/requests) shows durable jobs for the selected project, newest first. It includes API and playground work. Each row represents an admitted model operation, not every HTTP polling request or failed authentication attempt.

## Find a request [#find-a-request]

Filter the loaded rows by request ID or status, and use the type selector to show inference or internalization. The initial view loads 50 records. **Load earlier requests** fetches another page.

The text and type filters operate on loaded records. If an older job is missing, load more history or read its known ID through the API. An empty filtered table is not proof that the operation never existed.

## Read the row [#read-the-row]

| Column  | Interpretation                                      |
| ------- | --------------------------------------------------- |
| Request | A shortened job ID; open it for the full identifier |
| Type    | Inference or internalization                        |
| Status  | The latest durable lifecycle state                  |
| Tokens  | Recorded input plus output inference tokens         |
| Cost    | Settled charge, or Pending while unresolved         |
| Started | Admission time                                      |

Internal training tokens are not customer inference-token usage. A successful internalization can therefore have a fixed fee without a matching count of teacher or training tokens in this table.

## Open details [#open-details]

Select a request ID to inspect the full ID, status, phase, adapter snapshot, token counts, reserved amount, charged amount, and start time. The result block contains the answer or candidate metadata when available.

For learning, the Adapter field is the parent selected at admission. The new candidate is inside the result. For inference, Adapter is the version used for that answer. This distinction is useful when an update finishes during another request.

The console's request ID is the durable job ID. The `X-Request-Id` returned by an API call identifies a single HTTP attempt and is a separate diagnostic value. Keep both when reporting transport problems.

## Diagnose by state [#diagnose-by-state]

Queued or running means continue observing. Ready means a learning candidate exists, so check activation. Rejected means the candidate failed validation. Failed means a known error; inspect the result, error, and settled charge. Reconciliation required means stop resubmitting and preserve the job for investigation.

Copying the ID is enough for most support triage. Avoid sending the result block when it contains private model content. Use [safe diagnostics](/docs/troubleshooting/diagnostics) to prepare a small report, and [job recovery](/docs/troubleshooting/jobs) to decide what action is appropriate.


---

# Project settings

Source: https://convergingthought.com/docs/product/settings

> Change the project label, control monthly spending, and inspect recent administrative activity.



[Settings](/internalize/app/settings) applies to the selected project. It contains the project name, immutable ID, monthly spending limit, and recent activity log. It is not a model-configuration page: adapter rank, optimizer settings, and provider credentials are managed by the platform.

## Name and identifier [#name-and-identifier]

Change the project name when its label needs to be clearer. Names must be nonempty and no longer than 80 characters. Renaming does not change the project ID, API keys, subject names, or historical job identifiers.

Use the copy control next to Project ID when configuring your own operational records or requesting support. The ID is useful for correlation but does not grant access by itself. Public model requests select the project from their API key rather than a project-ID body field.

## Monthly spending limit [#monthly-spending-limit]

Enter a nonnegative USD amount and save. The platform converts it to integer micro-USD and enforces a hard admission limit that includes current settled spend and running reservations. The control resets by calendar month in UTC.

Set the limit to $0 to pause new paid admissions. This does not cancel already-admitted work, release its reservations, or prevent its eventual settlement. Lowering a limit below existing usage similarly blocks new work without rewriting the past.

The default for a new project is $100. This is a ceiling, not a subscription, credit grant, or promise to charge your card. You still need a prepaid balance, and automatic top-ups are not enabled.

## Activity log [#activity-log]

The recent activity table records changes such as project creation and updates, key creation and revocation, submissions, and adapter activation. It shows action, resource, and time. The current view is limited to the most recent 50 entries.

Use it to understand administrative changes, then inspect Activity for execution outcomes. The activity view is not a complete export of every read, every HTTP attempt, or every provider operation. It intentionally does not contain prompt bodies or API secrets.

## Boundaries [#boundaries]

The current console uses project ownership and scoped API keys. It does not expose team invitations, custom role definitions, single sign-on configuration, per-subject budgets, or self-service data deletion. Do not assume settings from another platform are available here.

See [Spending limits](/docs/billing/limits) for the exact budget behavior and [Data handling](/docs/concepts/data-handling) for access or deletion requests. If a saved value appears wrong, verify the selected project before changing it again.


---

# Read usage

Source: https://convergingthought.com/docs/product/usage

> Understand settled token counts, call totals, and daily spend.



[Usage](/internalize/app/usage) summarizes settled work for the recent 30-day period. It combines API and playground operations within the selected project. It does not estimate future spend from pending jobs or treat reserved credit as already consumed.

## Summary metrics [#summary-metrics]

**Total spend** is the sum of settled charges in the displayed period. **Tokens** combines recorded input and output inference tokens. **Requests** counts settled inference and internalization jobs. **Internalizations** counts settled learning operations, including rejected or failed ones, so it is not a count of active adapters.

These distinctions matter when estimating cost. Call counts do not determine charges: learning uses variable amounts of compute. Use actual settled charges for accounting and inspect request outcomes for success rates.

## Daily view [#daily-view]

The chart shows daily spend in USD using UTC dates. The table breaks out input tokens, output tokens, internalization calls, and cost. Output includes generated reasoning, even though raw reasoning is not returned to the caller.

Input and output totals include teacher, scoring, and validation calls as well as customer inference. Training tokens are reported separately on each job. Verified cache hits are a subset of input. The job’s compute and hosting amounts explain its settled charge.

## Settlement timing [#settlement-timing]

Daily usage is recorded when a job settles. A job admitted before midnight and completed after midnight appears in the later UTC day's settled usage. Similarly, work crossing a month boundary affects the month in which its charge settles.

The monthly spending limit is a calendar-month control in Settings. The Usage workspace is a recent-period view. Their totals need not match when the displayed recent period spans two calendar months.

## Pending and zero usage [#pending-and-zero-usage]

A running request may hold credit without appearing as settled spend. Read Billing for available and reserved balances, and Activity for the underlying job. A zero-token display on an unresolved job does not establish that the provider performed no work.

An empty chart is expected for a new project and for a hosted preview project that has not executed model work. The interface does not generate sample usage to fill the chart.

For exact per-operation accounting, read `billing.charged_microusd` and `billing.settled` on the job. Display rounding in a chart is not the ledger's precision. See [Credits and pricing](/docs/billing) for calculations and [Spending limits](/docs/billing/limits) for admission controls.


---

# SDKs and HTTP clients

Source: https://convergingthought.com/docs/sdk-reference/overview

> Choose a client without changing the public job, routing, or billing semantics.



Internalize's HTTP API is the public integration contract. The repository also contains `@internalize/sdk`, a private TypeScript workspace package. There is currently no publicly published npm package or Python SDK to install.

## Available paths [#available-paths]

| Integration              | Availability                              | What it provides                                                             |
| ------------------------ | ----------------------------------------- | ---------------------------------------------------------------------------- |
| Direct HTTP              | Public contract                           | All seven documented endpoints                                               |
| TypeScript workspace SDK | Authorized repository checkouts           | Typed writes, job reads, activation, polling, and agent tool helpers         |
| Python example           | Copyable standard-library code            | HTTP submission and bounded polling                                          |
| Generated client         | Generate from OpenAPI in your own project | Transport and schema types; application semantics remain your responsibility |

Do not run an unverified `npm install internalize` or similarly named package assuming it is this product. Follow the workspace instructions when you have repository access, or use the documented HTTP calls from your own backend.

## Client responsibilities [#client-responsibilities]

Whichever client you choose, your application owns the mapping from users to subjects, the decision to learn a passage, and the durable identity of each operation. Store the idempotency key before submission and the job ID immediately after admission. A process-local variable alone is not enough for recovery after a server restart.

The client must distinguish transport errors from job outcomes. A `202` response is acceptance, a completed polling helper can return a failed job, and `ready` does not necessarily mean activated. Preserve these distinctions in your application state rather than reducing everything to a boolean success flag.

## Base URL and credentials [#base-url-and-credentials]

Use `https://convergingthought.com` as the origin. The TypeScript client accepts that origin as `baseUrl` and supplies `/v1` paths itself. HTTP examples use `INTERNALIZE_BASE_URL` and append the endpoint explicitly.

Keep the API key in a server secret store. Do not expose it to a browser, agent prompt, repository, or client-side environment variable. The public API rejects requests with an Origin header.

## Method coverage [#method-coverage]

The workspace SDK exposes `internalize`, `infer`, `job`, `wait`, and `activate`. It does not currently include convenience methods for listing jobs, subjects, or models. Use authenticated HTTP for those reads; do not infer unimplemented method names from other providers' clients.

Continue with [TypeScript](/docs/sdk-reference/typescript), [Python](/docs/sdk-reference/python), and [Polling and recovery](/docs/sdk-reference/polling). All live examples require an execution-enabled, funded project; the current [hosted preview](/docs/getting-started/availability) remains gated.


---

# Polling and recovery

Source: https://convergingthought.com/docs/sdk-reference/polling

> Keep one durable operation through retries, process restarts, and long-running work.



A reliable integration treats submission and observation as separate activities. Submission establishes the job. Observation reads the same job until its outcome is known. A browser refresh or an impatient caller should not create a second model operation.

## Store the intent before sending [#store-the-intent-before-sending]

Create an application operation record with an idempotency key, subject, kind, and the exact payload or a secure reference to it. Persist it before making the POST. When admission returns, add the job ID and status URL to that same record.

```text
Application intent
  operation_key   stable before first POST
  payload_ref     authorized storage for the original input
  job_id          saved as soon as admission returns
  last_status     most recent observation
  next_check_at   when the application should read again
```

This record is your recovery boundary. Keep private source content in appropriate storage rather than copying it into an ordinary task log. Idempotency keys should also be opaque, not encoded user messages.

## Observe with bounded backoff [#observe-with-bounded-backoff]

Read the job immediately after admission, then increase the delay between reads. One second growing to 15 seconds is suitable for the current asynchronous workflow. Add jitter if many application workers might poll together. Use a maximum observation window so a request handler is not held open indefinitely.

If your hosting environment has short request deadlines, return the application operation ID to the caller and schedule later reads in your own durable worker. There is no requirement to keep the original HTTP connection open, and no public callback registration exists yet.

## Decide from the full state [#decide-from-the-full-state]

When `billing.settled` is true, inspect the terminal status and result. `succeeded` is an inference result. `ready` is a validated candidate and still needs an activation check. `failed` and `rejected` are known completed outcomes, not exceptions that automatically justify resubmission.

When `reconciliation_required` appears, stop the automatic paid workflow and preserve the job. You may continue occasional read-only observation, but do not release your own notion of the operation and start another one as a substitute.

## Recover each interruption [#recover-each-interruption]

| Interruption                    | Recovery                                               |
| ------------------------------- | ------------------------------------------------------ |
| POST timed out before receipt   | Repeat the original key and body to recover admission  |
| Polling GET failed              | Back off and read the same job                         |
| SDK polling deadline elapsed    | Save the ID and resume observation later               |
| User closed the browser         | Resume from your server's operation record             |
| Application worker restarted    | Load the saved intent and receipt                      |
| Candidate activation conflicted | Read the subject and make an explicit release decision |
| Provider outcome is ambiguous   | Keep the reservation and request reconciliation        |

## Cancellation and user-facing status [#cancellation-and-user-facing-status]

The API has no cancellation endpoint. An AbortSignal only stops the client from waiting. If a user dismisses a loading panel, label that action as closing or stopping observation rather than cancelling the model job.

Expose useful distinctions in your own UI: queued, running, completed, learning ready but not active, and needs investigation. Show a recoverable operation ID so a user can return later. Avoid a generic timeout message that encourages them to click a button that starts the same paid work again.

## Retry ceilings [#retry-ceilings]

Bound transport retries by both count and elapsed time. After persistent failures, retain the intent and mark it for later inspection. Do not change payloads in-place under an existing key; corrections are deliberate new operations with new identities.

For manual adapter activation, use read-after-write recovery instead of the model-job idempotency pattern. Read [Activate an adapter](/docs/api-reference/activation) for that distinct contract.


---

# Python over HTTP

Source: https://convergingthought.com/docs/sdk-reference/python

> Submit and poll using Python's standard library, without an unpublished SDK dependency.



The public API works with any server-side HTTP client. This guide uses Python's standard library so the example does not depend on an Internalize package that has not been published. It demonstrates transport behavior; your application should provide durable storage for operation keys and job IDs.

## Configure the environment [#configure-the-environment]

Set `INTERNALIZE_API_KEY` in your server environment. The example defaults to the production origin and allows `INTERNALIZE_BASE_URL` to override it for an authorized deployment. Do not put a real key directly in the script.

```python title="client.py"
import json
import os
import time
import urllib.error
import urllib.request

BASE_URL = os.environ.get(
    "INTERNALIZE_BASE_URL", "https://convergingthought.com"
).rstrip("/")
API_KEY = os.environ["INTERNALIZE_API_KEY"]


class ApiError(Exception):
    def __init__(self, status, body):
        error = body.get("error", {})
        self.status = status
        self.code = error.get("code", "unknown_error")
        self.request_id = error.get("request_id")
        self.retryable = error.get("retryable", False)
        super().__init__(self.code)


def request(path, body=None, operation_key=None):
    headers = {"Authorization": f"Bearer {API_KEY}"}
    data = None
    if body is not None:
        headers["Content-Type"] = "application/json"
        data = json.dumps(body).encode("utf-8")
    if operation_key is not None:
        headers["Idempotency-Key"] = operation_key
    req = urllib.request.Request(BASE_URL + path, data=data, headers=headers)
    try:
        with urllib.request.urlopen(req, timeout=30) as response:
            return json.load(response)
    except urllib.error.HTTPError as error:
        try:
            body = json.loads(error.read())
        except (ValueError, UnicodeDecodeError):
            body = {}
        raise ApiError(error.code, body) from None


def wait_for_job(job_id, timeout_seconds=600):
    deadline = time.monotonic() + timeout_seconds
    delay = 1.0
    while time.monotonic() < deadline:
        job = request("/v1/jobs/" + job_id)
        if job["status"] == "reconciliation_required":
            raise RuntimeError(f"Reconciliation required for {job_id}")
        if job["billing"]["settled"]:
            return job
        remaining = deadline - time.monotonic()
        if remaining <= 0:
            break
        time.sleep(min(delay, remaining))
        delay = min(15.0, delay * 1.5)
    raise TimeoutError(f"Resume polling job {job_id}; it was not cancelled")
```

The 30-second timeout applies to an individual HTTP request. The polling deadline bounds observation, although an in-flight read can complete after that deadline. A timeout does not cancel paid work. This helper intentionally leaves HTTP retry decisions to the calling application.

## Learn and read [#learn-and-read]

```python title="example.py"
from client import request, wait_for_job

# Persist this key before sending. Reuse it only for this exact operation.
operation_key = "support-policy-python-001"
accepted = request(
    "/v1/internalizations",
    {
        "subject_id": "support-python",
        "content": "Northstar accepts returns within 30 days. Return labels are prepaid.",
        "max_cost_microusd": 5000000,
        "activate": True,
    },
    operation_key,
)

# Save accepted['id'] in your application before waiting.
job = wait_for_job(accepted["id"])
if job["status"] != "ready" or not (job.get("result") or {}).get("activated"):
    raise RuntimeError(f"Learning needs review: {job['id']}")

answer_receipt = request(
    "/v1/inferences",
    {
        "subject_id": "support-python",
        "messages": [{"role": "user", "content": "Who pays for return labels?"}],
        "max_output_tokens": 2048,
    },
    "support-python-question-001",
)
answer = wait_for_job(answer_receipt["id"])
if answer["status"] != "succeeded":
    raise RuntimeError(f"Inference needs review: {answer['id']}")
print(answer["result"]["text"])
```

The final print is suitable for a private terminal demonstration. A production service should return the answer to its authorized caller and keep model content out of general-purpose logs.

## Recover an interrupted process [#recover-an-interrupted-process]

If a saved job ID exists, call `wait_for_job` with that ID. If admission may have succeeded but no receipt was saved, repeat `request` with the original operation key and unchanged body. Do not generate a new key merely because the process restarted.

For `429`, respect Retry-After in a production transport wrapper. For authorization or validation failures, correct the cause before trying again. For reconciliation, keep the original operation and involve support. See [Errors](/docs/api-reference/errors) and [Polling](/docs/sdk-reference/polling).

The [preview deployment](/docs/getting-started/availability) will reject model admission until execution is enabled. A correctly structured script can therefore receive `preview_only`; that response is not evidence of a Python transport problem.


---

# TypeScript client

Source: https://convergingthought.com/docs/sdk-reference/typescript

> The exact interface of the private workspace SDK, with explicit operation identity and result checks.



`@internalize/sdk` is a private workspace package in the Internalize monorepo. Its source is TypeScript and its exports point to the source modules. Use it from an authorized workspace with a TypeScript-aware toolchain. It is not a public npm installation target.

## Construct the client [#construct-the-client]

```ts
import { InternalizeClient, InternalizeError } from "@internalize/sdk";

const client = new InternalizeClient({
  baseUrl: "https://convergingthought.com",
  apiKey: process.env.INTERNALIZE_API_KEY!,
});
```

The constructor accepts `baseUrl`, `apiKey`, an optional `tenantId`, and an optional `fetch` implementation for controlled transport or tests. It stores the URL origin, so a path prefix in `baseUrl` is not preserved. HTTPS is required except for localhost addresses used in local testing.

## Select a customer [#select-a-customer]

Use `const customer = client.forTenant(authorizedTenantId)` before learning or inference. The returned client carries `X-Internalize-Tenant` on every operation, including `job`, `wait`, and `activate`. It leaves the shared client unchanged and cannot be rebound to another tenant. You can also set `tenantId` in the constructor.

If you supply `tenant_id` directly in an input body, use a client bound to that same tenant when polling the result. A body tenant and client header must agree. Bind identity in trusted server code, outside model arguments. See [Serve multiple customers](/docs/guides/multitenancy).

## Submit and inspect learning [#submit-and-inspect-learning]

```ts
// Persist this operation key in your application before the first attempt.
const operationKey = "support-policy-release-001";
const accepted = await client.internalize(
  {
    max_cost_microusd: 5_000_000,
    subject_id: "support-policy",
    content:
      "Northstar accepts returns within 30 days of delivery. Return labels are prepaid.",
    activate: true,
  },
  operationKey,
);

// Persist accepted.id immediately so another process can resume the wait.
const learned = await client.wait(accepted.id, { timeoutMs: 600_000 });
if (learned.status !== "ready" || !learned.result?.activated) {
  throw new Error(`Learning needs review: ${learned.status}`);
}
```

`wait` returns a job when `billing.settled` becomes true. It does not throw merely because the settled status is `failed` or `rejected`. Check the status and activation result explicitly, as above, and retain the original job for diagnosis.

## Ask through the same subject [#ask-through-the-same-subject]

```ts
const acceptedAnswer = await client.infer(
  {
    subject_id: "support-policy",
    messages: [{ role: "user", content: "Who pays for a return label?" }],
    max_output_tokens: 2048,
    temperature: 0,
  },
  "support-label-question-001",
);

const answer = await client.wait(acceptedAnswer.id);
if (answer.status !== "succeeded") {
  throw new Error(answer.error?.code ?? answer.status);
}
console.log(answer.result?.text);
```

The logging line is for a private local example. In a deployed application, return the answer through your authorized response path rather than writing model content to general analytics. Check `answer.adapter_version` when the exact learned release matters.

## Methods [#methods]

| Method        | Arguments                                           | Result                             |
| ------------- | --------------------------------------------------- | ---------------------------------- |
| `forTenant`   | Authorized tenant ID                                | Independent namespace-bound client |
| `internalize` | Input, explicit idempotency key                     | `{ id, status_url }`               |
| `infer`       | Input, explicit idempotency key                     | `{ id, status_url }`               |
| `job`         | Job ID, optional AbortSignal                        | Current `Job`                      |
| `wait`        | Job ID, optional wait options                       | Settled `Job`, or polling error    |
| `activate`    | Candidate version, expected current version or null | `{ version_id, revision }`         |

`wait` options are `signal`, `timeoutMs`, and `onUpdate`. The default timeout is one hour. Polling starts at one second and increases by a factor of 1.5 up to 15 seconds. `onUpdate` receives each observed job; avoid forwarding its result content into telemetry.

## Errors and cancellation [#errors-and-cancellation]

HTTP failures throw `InternalizeError` with `code`, `status`, `requestId`, and `retryable`. Transport failures can throw ordinary fetch errors. `wait` throws `poll_timeout` when its deadline expires and `reconciliation_required` when the job needs investigation.

An AbortSignal stops a read or wait; it does not cancel model execution. Neither `infer` nor `internalize` accepts an AbortSignal argument in the current client. The SDK never retries writes automatically and does not automatically retry a failed polling HTTP request.

Catch errors at your durable application boundary, store the job ID, and resume with `job` or `wait` when appropriate. Do not wrap submission in an unbounded retry that generates a fresh operation key. See [Polling and recovery](/docs/sdk-reference/polling) for the full decision flow.


---

# Service terms

Source: https://convergingthought.com/docs/terms

> Preview service conditions.



Internalize is a preview developer service. Use it only for content and applications you are authorized to operate. Keep account credentials private and review generated outputs before relying on them.

Usage is prepaid with a $10 minimum purchase. Published rates apply to settled usage. Taxes are shown at checkout. Learning and validation do not guarantee perfect model recall or correctness.

Contact [team@convergingthought.com](mailto:team@convergingthought.com) for support, billing questions, or account closure. Final production commercial terms and data-processing terms must be supplied before a public paid launch.


---

# Access and setup problems

Source: https://convergingthought.com/docs/troubleshooting/access

> Diagnose credentials, scopes, preview gates, and project mismatches before changing code.



Start with the exact HTTP status and `error.code`. Similar-looking setup failures can require different actions: an invalid key, insufficient scope, unavailable execution, and an unfunded project are not interchangeable.

## Unauthorized [#unauthorized]

For `401 unauthorized`, confirm that the server sends `Authorization: Bearer` followed by the full key. A displayed key prefix is not enough. Check whether the key expired or was revoked and whether the environment variable contains an accidental newline, quote, or placeholder.

Use a read-only `/v1/models` request as an initial authentication check. Do not paste the key into a browser URL, a support message, or an online request debugger. If the full key was lost, create and install a replacement through the console.

## Permission denied [#permission-denied]

`insufficient_scope` means authentication succeeded but the key lacks a required permission. An inference or learning key can poll jobs it submitted; project-wide inspection requires `read`. A learning key needs `internalize`; manual activation needs `adapters`.

`server_keys_only` means an Origin header reached the public API. Move the call behind your application's authenticated backend. A browser-visible key should be revoked if it was exposed; removing a frontend header does not make that key private.

## Preview only [#preview-only]

`preview_only` is the hosted deployment's execution gate. It is not fixed by creating more keys, using a different subject, or completing a purchase preview. Read [Preview availability](/docs/getting-started/availability) for the current state.

Guest sessions are for exploring the console. They are not a substitute for durable production account access. Email delivery is still pending setup in this deployment, so an unavailable email flow should not be diagnosed as an adapter or API problem.

## Wrong project or missing resource [#wrong-project-or-missing-resource]

A key chooses its owning project. Switching projects in the browser does not change a server's installed key. If a known job returns `not_found`, verify the key's project and the full job ID before assuming the job was deleted.

Subject names are case-sensitive and project-local. `support` in one project is not the same model memory as `support` in another. Compare the submitted subject with the intended server-side mapping.

## Invalid request [#invalid-request]

For `422`, check JSON syntax, content type, idempotency-key format, unknown fields, and endpoint-specific limits. An inference must end with a user message or a completed tool-result block. A learning passage must be at least 20 string-length units. Provider clients should use `/v1/chat/completions`; the native `/v1/inferences` endpoint has a different request shape.

Use the [API reference](/docs/api-reference) rather than repeatedly changing unrelated settings. If the minimal valid request still fails, send the request ID and redacted request shape through the [diagnostics procedure](/docs/troubleshooting/diagnostics).


---

# Safe diagnostics

Source: https://convergingthought.com/docs/troubleshooting/diagnostics

> Collect the smallest useful report without exposing API secrets or model content.



A useful report links the observed behavior to a specific request or job. Start with identifiers and state. Most access, routing, and billing issues do not require sending the underlying learning passage or generated answer.

## Include [#include]

* UTC timestamp and the platform origin.
* Client language and, if relevant, workspace SDK version.
* HTTP method, route template, status, and `error.code`.
* The HTTP `X-Request-Id` and durable job ID when available.
* Authorized project ID, job status, phase, and last update time.
* Adapter version, candidate version, and activation outcome for routing issues.
* Whether the attempt reused the original idempotency key and unchanged body.
* Expected behavior and the shortest steps that reproduce the problem.

For a spending discrepancy, include the integer reserved and charged amounts and whether the job settled. For a payment discrepancy, include the order reference and selected project. Identify which screen or API response showed the unexpected value.

## Leave out [#leave-out]

Do not include bearer tokens, full API keys, cookies, passwords, provider credentials, payment-card details, database URLs, or signed internal URLs. Redact learning sources, inference messages, completions, and raw reasoning from the initial report.

Avoid screenshots of an unmasked key dialog or a private result block. An opaque subject identifier is preferable to a subject name that itself reveals a customer's identity. Report that an idempotency key was reused, rather than exposing a key that encodes private application information.

## Example report [#example-report]

```text
UTC: 2026-09-24T10:00:00Z
Origin: https://convergingthought.com
Client: server-side TypeScript
Operation: POST /v1/internalizations
HTTP status: 202
HTTP request ID: <request ID>
Job ID: <job ID>
Latest status / phase: reconciliation_required / <phase>
Settled: false
Retry: no replacement submitted; original operation key preserved
Observed: job requires investigation and still holds its reservation
Expected: a known terminal outcome or a recovery explanation
```

This is a template, not a real incident. Substitute actual identifiers only in an authorized support channel. Keep your own private source and evaluation evidence available in case a later investigation specifically needs a minimal redacted example.

## Escalate [#escalate]

Contact [team@convergingthought.com](mailto:team@convergingthought.com). Explain the affected workflow and include one recent request or job ID. Repeated blind retries create noise and can start additional paid work, so preserve the first operation while the issue is being investigated.

If the issue is a content-quality failure rather than transport or routing, first follow [Knowledge troubleshooting](/docs/troubleshooting/knowledge). If the model operation is ambiguous, follow [Job recovery](/docs/troubleshooting/jobs) and do not attempt to reconcile usage by guessing.


---

# Stalled and failed jobs

Source: https://convergingthought.com/docs/troubleshooting/jobs

> Recover the original operation without duplicating paid inference or learning.



Find the durable job ID first. If your application saved the receipt, read that job directly. If the submission response was lost, recover it by repeating the original idempotency key and unchanged payload.

## Queued or running for longer than expected [#queued-or-running-for-longer-than-expected]

Read the latest status, phase, and `updated_at`. The API does not promise a fixed execution duration or expose a percentage-complete value. Continue bounded observation and preserve the job ID rather than submitting a replacement because a loading indicator lasted too long.

A client or SDK polling timeout only ends observation. Resume with the same ID. The public API has no cancellation endpoint, so closing the browser or aborting a fetch is not evidence that work stopped.

If the state remains unexpectedly unchanged, collect the job ID, timestamp, phase, and last update time for support. Do not send the learning passage or entire answer as the first diagnostic step.

## Failed or rejected [#failed-or-rejected]

For `failed`, inspect `error.code`, settlement, and any result metadata. A known inference failure can still carry billable usage if generation occurred. Zero visible output is not the same as zero tokens.

For `rejected`, the candidate did not pass the learning gate and the active subject was not replaced. Review source clarity and the independent evaluation before making a deliberate new update. The fixed successful-internalization fee does not apply to a rejected job.

A new attempt after a known terminal outcome is a new operation. Use a new key only after deciding that new work is intended, not as an automatic reaction to every error.

## Reconciliation required [#reconciliation-required]

This state means the platform cannot safely establish the outcome or complete usage. A worker may have lost its lease after execution started, a dispatch may be unconfirmed, or a provider result may be incomplete.

Keep the original job and reservation. Do not treat the reserved amount as released, infer a refund, or create a replacement job. An operator must inspect durable execution receipts and provider evidence to settle the original operation without replaying it.

Your application can mark the operation as needing investigation and continue occasional read-only checks. If an operator resolves it, the same job can later expose a known terminal outcome and settlement.

## Ready but knowledge appears unchanged [#ready-but-knowledge-appears-unchanged]

Inspect `result.activated`. A saved candidate is not always active. If `activation_conflict` is true, another update changed the subject during training. Read current routing and make a deliberate release decision.

If activation succeeded, compare the later inference job's `adapter_version`. It may have been admitted before the route changed, or it may target another subject or project. Only after confirming the route should you diagnose model quality.

## Balance held after an interruption [#balance-held-after-an-interruption]

Open reservations remain held until the outcome is settled. Reconciliation is intentionally conservative about ambiguous paid work. Look up the pending jobs in Activity and match their reservations to Billing before reporting an unexplained balance discrepancy.

Use [safe diagnostics](/docs/troubleshooting/diagnostics) for escalation and [Polling and recovery](/docs/sdk-reference/polling) to prevent duplicate work in your integration.


---

# Knowledge and routing problems

Source: https://convergingthought.com/docs/troubleshooting/knowledge

> Separate a wrong route, a failed activation, expired hosting, and a genuine learning failure.



When an answer does not reflect the taught knowledge, first establish which weights produced it. Adding the source to the prompt can hide the cause rather than prove the adapter works.

## Check identity and timing [#check-identity-and-timing]

Compare the project selected by the key, the exact case-sensitive subject ID, and the inference job's `adapter_version`. Then inspect the learning job's candidate and activation result.

An inference admitted before activation keeps the previous version even if it completes afterward. A new subject starts from base. A key from another project refers to a different subject namespace. These cases can all look like forgotten knowledge while following the intended routing rules.

## Candidate saved but inactive [#candidate-saved-but-inactive]

`ready` means validation passed and a candidate was saved. If learning used `activate: false`, activation is still your release process's responsibility. If `activation_conflict` is true, another version changed while training.

Read the subject and review the candidate before manual activation. Do not automatically replace the expected-version value with whatever is current; that can overwrite another release. See [Versions and routing](/docs/concepts/versions-and-routing).

## Correct route, incorrect answer [#correct-route-incorrect-answer]

Check whether the source actually contains the required fact, definition, or exception. The curriculum cannot ground a missing warehouse address or resolve two conflicting policies without a clear basis. Review whether the question asks for an application that was not adequately represented by the source.

Run several independent tests: direct recall, a paraphrase, an exception, and an unsupported question. Keep the source and expected answers out of the inference context. Inspect output truncation separately from factual correctness.

A passing built-in validation result covers its own checks, not all future behavior. Use the [evaluation guide](/docs/guides/evaluation) to decide whether to revise the passage, narrow the intended use, or restore a retained version.

## Earlier knowledge changed [#earlier-knowledge-changed]

Later learning begins from active parent weights, but that is not a guarantee of perfect retention. Test old behaviors after updates. If several jobs were admitted from the same parent, their knowledge is not automatically merged; one candidate may omit another concurrent update.

Serialize dependent updates and retain a source manifest so you know what each release intended to add. A correction is a new learning operation, not verified erasure of an earlier fact.

## Expired adapter [#expired-adapter]

`adapter_expired` means the required retained state is no longer valid for routing or activation. Inspect `expires_at` and the current subject. The platform does not silently fall back to base or another user's adapter.

Contact support with the version and source job IDs. Restoration requires verified retained state; do not claim the knowledge remains hosted if that state cannot be recovered. Keep canonical sources outside the model so a deliberate rebuild is possible if needed.
