You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
"rationale": "Matches the reference; arithmetic is correct."
345
+
}
346
+
```
347
+
348
+
-`rating` (required, `"up"` or `"down"`) — binary bucket used by
349
+
training-data selectors.
350
+
-`score` (optional, `0.0-1.0`) — numeric grade when the principal is a
351
+
judge; ignored by bucket-oriented selectors.
352
+
-`criteria` (optional dict of string → float) — per-axis grades.
353
+
-`rationale` (optional, max 10,000 chars) — free-form text the judge
354
+
produced.
355
+
-`reason` (optional, max 500 chars) — the shorter, human-UI-friendly
356
+
explanation; interchangeable with `rationale` on the server side.
357
+
358
+
**Response (201):**
359
+
```json
360
+
{
361
+
"event_id": 42,
362
+
"event_type": "user.feedback",
363
+
"source": "judge"
364
+
}
365
+
```
366
+
367
+
`source` is `"judge"` when the caller presented a service-account token
368
+
and `"user"` when the caller presented an interactive JWT. Stored on
369
+
the event's JSONB payload so downstream training-data selection and
370
+
dashboards can weight the two independently.
371
+
372
+
**Idempotency.** Dedupe is keyed on `(session_id, event_id, principal)`
373
+
where `principal` is the caller's `user_id` for JWT callers and
374
+
`service_account_id` for SA callers. A retry from the same principal
375
+
returns the original feedback event unchanged; feedback from a user
376
+
and from a judge on the same turn coexist as two independent events.
377
+
378
+
## Prompts (API Channel)
379
+
380
+
Fire-and-forget prompt submission for non-interactive clients. Requires a service-account token. Results are read back from the `events` table by `session_id`. See [Channels / API](../channels/api.md) for the end-to-end pipeline workflow.
381
+
382
+
### `POST /v1/api/prompts`
383
+
384
+
Submit a single prompt.
385
+
386
+
**Request:**
387
+
```json
388
+
{
389
+
"prompt": "Write a haiku about distributed systems.",
-`idempotency_key` (optional, max 200 chars) -- two submissions with the same key + org resolve to the same session; the second returns `deduplicated: true` and enqueues no new work.
397
+
-`metadata` (optional dict) -- stored on `sessions.config['pipeline_metadata']`; the pipeline joins results back to its dataset via this field.
398
+
399
+
**Response (202):**
400
+
```json
401
+
{
402
+
"session_id": "8f...",
403
+
"event_id": 42,
404
+
"deduplicated": false,
405
+
"error": null
406
+
}
407
+
```
408
+
409
+
### `POST /v1/api/prompts:batch`
410
+
411
+
Submit up to 100 prompts in one round-trip. Each entry is processed independently; partial failures surface per-slot, not as a whole-request 500 (unless every entry fails).
Input order is preserved so the caller can zip results back to its input rows.
434
+
326
435
## Admin
327
436
328
437
These endpoints require admin permissions.
@@ -347,6 +456,43 @@ Create a user in an organization.
347
456
348
457
Install the Slack bot for an organization.
349
458
459
+
### Service Accounts (Admin) {#service-accounts-admin}
460
+
461
+
Issue and revoke service-account tokens that authenticate the API channel. All endpoints require the `admin` permission. Tokens have the prefix `surg_sk_`; the raw value is returned once on creation and is not recoverable.
Store the `token` immediately -- only the `token_prefix` is persisted afterwards.
487
+
488
+
#### `GET /v1/admin/service-accounts?org_id={id}`
489
+
490
+
List service accounts for an org. `token` is never returned.
491
+
492
+
#### `DELETE /v1/admin/service-accounts/{id}`
493
+
494
+
Revoke a service account. Immediate in the revoking process; peer API/worker processes converge within 60 seconds (the in-memory auth cache's TTL). A second delete on the same id returns 404.
Copy file name to clipboardExpand all lines: docs/appendices/glossary.md
+3-1Lines changed: 3 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,8 @@
4
4
|---|---|
5
5
|**ABAC**| Attribute-Based Access Control. Policy rules that evaluate attributes of the user, session, tool arguments, or environment to make access decisions. Example: "allow `refund_user` only if `amount < 1000`". |
6
6
|**AGT**| Agent Governance Toolkit. Microsoft's open-source library for agent policy enforcement, MCP security scanning, and capability modeling. Surogates uses AGT's `PolicyEngine`, `MCPSecurityScanner`, and `CapabilityModel`. |
7
-
|**Channel**| The user-facing interface. Surogates has no CLI. Users interact through channels: the web chat UI and Slack. Each channel has an adapter that normalizes platform messages into the internal API. |
7
+
|**API Channel**| The programmatic channel. Non-interactive clients (synthetic-data pipelines, batch jobs) submit prompts via `POST /v1/api/prompts` with a service-account token. Sessions have `channel="api"` and no user identity; results are read directly from the `events` table. |
8
+
|**Channel**| The user-facing interface. Surogates has no CLI. Users interact through channels: web, Slack, Telegram, and the API channel for programmatic clients. Each has an adapter (or, for web/API, a REST endpoint set) that normalizes inbound messages into the internal API. |
8
9
|**Channel Identity**| A mapping between a platform-specific user ID (e.g., Slack user `U03ABCDEF`) and an internal Surogates user. Enables cross-channel session sharing. |
9
10
|**Cursor**| The last fully-processed event ID for a session. Used for crash recovery -- the new worker replays events after the cursor. Also used by SSE clients to resume event streams without data loss. |
10
11
|**Delivery Outbox**| A PostgreSQL table that acts as a durable queue for outbound messages. Channel adapters claim rows, send messages, and mark them as delivered. Redis nudges are a latency optimization, not the source of truth. |
@@ -20,6 +21,7 @@
20
21
|**Org**| Organization. The top-level tenant boundary. Each org has its own users, skills, memory, credentials, MCP servers, and policies. |
21
22
|**Saga**| A tracked sequence of tool calls with automatic rollback. When a step fails, previously completed steps are compensated in reverse order -- builtin tools via filesystem checkpoints, MCP tools via declared undo operations. Named after the [saga pattern](https://microservices.io/patterns/data/saga.html) from distributed systems. |
22
23
|**Sandbox**| An isolated execution environment where the LLM's generated code runs. In development: a subprocess in a temp directory. In production: a dedicated K8s pod with s3fs-fuse workspace mount. Also called "the hands". |
24
+
|**Service Account**| An org-scoped principal used by non-interactive clients to authenticate against the API channel. Issued by an admin via `POST /v1/admin/service-accounts`; produces a long-lived `surg_sk_...` bearer token that is accepted only on `/v1/api/*` routes and carries no user identity. |
23
25
|**Session**| A conversation between a user and an agent. Backed by an append-only event log in PostgreSQL. Sessions survive crashes -- any worker can resume from the last event. |
24
26
|**Session Source**| Metadata about where a message came from: platform, chat ID, chat type, user ID, thread ID. Used to route messages to the correct session. |
25
27
|**Skill**| A reusable, prompt-based behavior defined in a `SKILL.md` file. Skills are loaded from three layers (platform > org > user) with last-wins precedence. |
The API channel is a programmatic, fire-and-forget interface for non-interactive clients -- synthetic data generation pipelines, batch evaluation jobs, and any other workload that submits prompts from outside the web or messaging channels. Authentication is by org-scoped API key ("service-account token"); no user identity is involved.
4
+
5
+
The API channel is not a chat interface -- it accepts a prompt, creates a session, queues it for the worker, and returns the session identifier. Results are read directly from the `events` and `sessions` database tables.
6
+
7
+
## When to use it
8
+
9
+
| Use case | Example |
10
+
|---|---|
11
+
| Synthetic training-data generation | A pipeline iterates over dataset rows, submits each prompt as a session, and later sweeps the `events` table for `llm.response` rows to harvest completions. |
12
+
| Automated evaluations | A scorer submits thousands of prompts in parallel and reads `events.data` for downstream metrics. |
13
+
| Scheduled bulk work | A cron job dispatches org-wide prompt runs. |
14
+
15
+
Do **not** use the API channel for interactive experiences -- use the [web channel](web.md) instead, which streams tokens and tool calls live over SSE.
16
+
17
+
## Authentication
18
+
19
+
The client presents an API key in the `Authorization: Bearer` header. API keys have the prefix `surg_sk_` and are issued to an org by an admin:
20
+
21
+
```
22
+
POST /v1/admin/service-accounts
23
+
Authorization: Bearer <admin-jwt>
24
+
25
+
{
26
+
"org_id": "00000000-...",
27
+
"name": "dataset-gen-v1"
28
+
}
29
+
```
30
+
31
+
The raw token is returned **exactly once** in the response body (`token`). Store it immediately -- the server keeps only a SHA-256 hash and cannot recover the plaintext. List and revoke endpoints live under the same `/v1/admin/service-accounts` prefix.
32
+
33
+
API keys may only authenticate requests to routes under `/v1/api/*`. Presenting one anywhere else returns 403. Conversely, the `/v1/api/*` routes reject interactive JWTs so the two principal types stay cleanly separated.
34
+
35
+
## Submitting a prompt
36
+
37
+
```
38
+
POST /v1/api/prompts
39
+
Authorization: Bearer surg_sk_...
40
+
41
+
{
42
+
"prompt": "Write a haiku about distributed systems.",
43
+
"idempotency_key": "dataset-42/row-1337",
44
+
"metadata": {
45
+
"dataset_id": "ds_123",
46
+
"row_index": 1337,
47
+
"experiment": "baseline-v3"
48
+
}
49
+
}
50
+
```
51
+
52
+
Response (`202 Accepted`):
53
+
54
+
```json
55
+
{
56
+
"session_id": "8f...",
57
+
"event_id": 42,
58
+
"deduplicated": false
59
+
}
60
+
```
61
+
62
+
The worker picks the session off the Redis queue and processes it asynchronously. The pipeline owns the returned `session_id` and uses it to read results from the database.
63
+
64
+
### Idempotency
65
+
66
+
`idempotency_key` is an optional client-supplied string scoped per org. Two requests from the same org with the same key resolve to the **same** session:
67
+
68
+
- first call -> `deduplicated: false`, new session created
69
+
- second call -> `deduplicated: true`, original `session_id` returned, no new work queued
70
+
71
+
Use this to make pipeline retries safe under timeouts or restarts. Keys from different orgs do not collide.
72
+
73
+
### Metadata passthrough
74
+
75
+
Anything in `metadata` is persisted onto `sessions.config['pipeline_metadata']`. The pipeline joins results back to its source dataset by querying for sessions with specific metadata values -- no side-table required.
Each entry is accepted independently. The response preserves input order so callers can zip results back to their input rows. Up to 100 prompts per request.
92
+
93
+
## Reading results
94
+
95
+
Each submitted prompt becomes a session (`channel='api'`). The pipeline reads:
96
+
97
+
| Signal | Source |
98
+
|---|---|
99
+
| Final LLM answer |`events` rows with `type = 'llm.response'` for the session |
100
+
| Tool calls / tool results |`events` rows with `type IN ('tool.call', 'tool.result')`|
101
+
| Completion status |`sessions.status` (`active`, `idle`, `completed`, `failed`) |
The `v_session_messages` view returns conversation-shaped events in training-data format; the `v_response_feedback` and `v_tool_invocations` views expose related signals. See [docs/audit/views.md](../audit/views.md) for the full catalog.
106
+
107
+
## Recording judge feedback
108
+
109
+
Pipelines that run an automated judge over their outputs record the judge's grade by `POST /v1/api/sessions/{session_id}/events/{event_id}/feedback`, authenticated with the same service-account token. The endpoint accepts binary `rating` (required), a numeric `score`, per-axis `criteria`, and a free-form `rationale`. The stored event carries `source: "judge"` so downstream training-data selection can weight judge feedback independently from human thumbs. See [Appendix B: Feedback (API Channel)](../appendices/api-reference.md#feedback-api-channel) for the full schema and idempotency semantics.
110
+
111
+
## Interaction with other subsystems
112
+
113
+
-**Training data**: API sessions participate in `TrainingDataCollector` exports on the same footing as every other channel -- successful expert delegations and skill invocations from pipeline-submitted prompts are eligible for fine-tuning.
114
+
-**Idle reset**: the session-reset CronJob resets API sessions in place without running the memory-flush agent -- service accounts have no per-user memory.
115
+
-**Memory**: API sessions use the org-shared memory directory, not user-scoped memory.
116
+
-**Permissions**: API keys carry no permissions; access is scoped entirely by org membership. They cannot reach admin, auth, or any other `/v1/` routes.
0 commit comments