@1claw/openapi-spec 0.59.9 → 0.60.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +21 -1
  2. package/openapi.json +3532 -312
  3. package/openapi.yaml +2193 -63
  4. package/package.json +1 -1
package/openapi.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "1Claw API",
5
- "version": "0.59.9",
5
+ "version": "0.60.0",
6
6
  "description": "Secure secret management for AI agents. Provides vaults, secrets,\npolicy-based access control, agent identity, Intents API,\nsharing, billing, and audit logging. Automations (workflow_spec,\nwebhook tokens, event triggers, Assist), cloud runtimes with\ninteractive shell sessions, agent memory, and discovery.\n\n## Domains\n\n`api.1claw.co` is canonical: it is the OIDC issuer, the `aud` the API\nmints, and the first entry in `servers` — a generated client takes its\nbase URL from there, and the previous ordering pointed every SDK at the\ndomain the issuer had already left. `api.1claw.xyz` still answers and is\nstill accepted on token validation, because tokens minted before the\nmove carry it; it is never minted now.\n\nOne deliberate exception: the Shroud attestation identity token is\nrequested from GCP with `audience: https://api.1claw.xyz`, so\n`/v1/shroud/attestation` reports that as its `expected_audience`. That\nis accurate rather than stale — the audience is a verification contract\nwith anyone already checking the token, and moving it is a breaking\nchange for them, not a rename.\n\nAll endpoints require JWT Bearer authentication unless marked with\n`security: []`.\n",
7
7
  "contact": {
8
8
  "email": "ops@1claw.co"
@@ -1228,7 +1228,7 @@
1228
1228
  "Approvals"
1229
1229
  ],
1230
1230
  "summary": "Request human approval (agent-only)",
1231
- "description": "Agents can request policy changes or other sensitive actions that require human approval.",
1231
+ "description": "Ask a human to approve an action.\n\nTwo families of action are accepted:\n\n* the control-plane requests `access_request`, `policy_request`\n and `binding_request`, which widen the agent's own authority; and\n* business actions named `namespace.verb` (`refund.create`,\n `social.post`), whose meaning is carried by `summary` and `payload`.\n\nActions that 1Claw itself executes on approval — `policy_change`,\n`card_order`, `agent_transaction`, `agent_execution`,\n`agent_sign_intent` — are created by the platform and rejected here,\nbecause the summary a human reads would be agent-supplied while the\nside effect would not be.\n",
1232
1232
  "operationId": "requestApproval",
1233
1233
  "requestBody": {
1234
1234
  "required": true,
@@ -1245,7 +1245,9 @@
1245
1245
  "properties": {
1246
1246
  "action": {
1247
1247
  "type": "string",
1248
- "description": "Type of action (e.g. policy_change)"
1248
+ "maxLength": 64,
1249
+ "description": "Control-plane action, or a business action named `namespace.verb`.",
1250
+ "example": "refund.create"
1249
1251
  },
1250
1252
  "target_type": {
1251
1253
  "type": "string"
@@ -1255,15 +1257,35 @@
1255
1257
  },
1256
1258
  "summary": {
1257
1259
  "type": "object",
1258
- "description": "JSON payload describing the request"
1260
+ "description": "What the human is shown: title, body, and key-value fields.",
1261
+ "example": {
1262
+ "title": "Refund $49.99",
1263
+ "body": "Order #1234 arrived damaged."
1264
+ }
1265
+ },
1266
+ "payload": {
1267
+ "type": "object",
1268
+ "description": "What the action will actually do. The enforced risk tier and\nthe human-readable line are derived from this, not from\n`summary` — the two can disagree, and only this one describes\nwhat happens if the human approves.\n",
1269
+ "example": {
1270
+ "amount_usd": "49.99",
1271
+ "customer_email": "a.user@example.com"
1272
+ }
1259
1273
  },
1260
1274
  "reason": {
1261
1275
  "type": "string"
1262
1276
  },
1277
+ "declared_risk_tier": {
1278
+ "type": "integer",
1279
+ "minimum": 1,
1280
+ "maximum": 3,
1281
+ "description": "Advisory. The server derives the enforced tier from the agent's\n`action_approval_policy` and the payload, then takes the higher\nof the two: a caller may raise its own bar, never lower it.\nThe response returns both.\n"
1282
+ },
1263
1283
  "risk_tier": {
1264
1284
  "type": "integer",
1265
1285
  "minimum": 1,
1266
- "maximum": 5
1286
+ "maximum": 3,
1287
+ "deprecated": true,
1288
+ "description": "Former name for `declared_risk_tier`. Still accepted."
1267
1289
  }
1268
1290
  }
1269
1291
  }
@@ -2442,7 +2464,24 @@
2442
2464
  }
2443
2465
  },
2444
2466
  "400": {
2445
- "$ref": "#/components/responses/BadRequest"
2467
+ "description": "The name is empty or longer than 255 characters. Checked before the consensus gate and the quota, so a malformed request consumes neither an approval nor a rate-limit slot.",
2468
+ "content": {
2469
+ "application/json": {
2470
+ "schema": {
2471
+ "$ref": "#/components/schemas/ProblemDetails"
2472
+ }
2473
+ }
2474
+ }
2475
+ },
2476
+ "409": {
2477
+ "description": "A vault with that name already exists in this organization. Names are unique per org; this previously surfaced as a 500.",
2478
+ "content": {
2479
+ "application/json": {
2480
+ "schema": {
2481
+ "$ref": "#/components/schemas/ProblemDetails"
2482
+ }
2483
+ }
2484
+ }
2446
2485
  }
2447
2486
  }
2448
2487
  },
@@ -10295,13 +10334,14 @@
10295
10334
  }
10296
10335
  }
10297
10336
  },
10298
- "/v1/platform/apps/{appId}/templates/{template_id}/preview": {
10299
- "post": {
10337
+ "/v1/platform/apps/{appId}/fleets/{template_id}": {
10338
+ "get": {
10300
10339
  "tags": [
10301
10340
  "Platform"
10302
10341
  ],
10303
- "summary": "Preview resolved template spec",
10304
- "description": "Resolves `{{params.*}}` and `{{subject.*}}` placeholders in a template spec\nwithout provisioning resources. Useful for validating parameterized bootstrap templates.\n",
10342
+ "summary": "Fleet summary for a template",
10343
+ "description": "Every agent this template provisioned, as one cohort: how many there are, how they split across the template versions they were built from, and how many a previous rollout declined to touch. plt_ or user JWT.\n",
10344
+ "operationId": "getFleet",
10305
10345
  "security": [
10306
10346
  {
10307
10347
  "BearerAuth": []
@@ -10327,23 +10367,81 @@
10327
10367
  }
10328
10368
  }
10329
10369
  ],
10330
- "requestBody": {
10331
- "required": false,
10332
- "content": {
10333
- "application/json": {
10334
- "schema": {
10335
- "$ref": "#/components/schemas/TemplatePreviewRequest"
10370
+ "responses": {
10371
+ "200": {
10372
+ "description": "Fleet summary",
10373
+ "content": {
10374
+ "application/json": {
10375
+ "schema": {
10376
+ "$ref": "#/components/schemas/FleetSummaryResponse"
10377
+ }
10336
10378
  }
10337
10379
  }
10380
+ },
10381
+ "404": {
10382
+ "$ref": "#/components/responses/NotFound"
10338
10383
  }
10339
- },
10384
+ }
10385
+ }
10386
+ },
10387
+ "/v1/platform/apps/{appId}/fleets/{template_id}/agents": {
10388
+ "get": {
10389
+ "tags": [
10390
+ "Platform"
10391
+ ],
10392
+ "summary": "List the agents in a fleet",
10393
+ "operationId": "listFleetAgents",
10394
+ "security": [
10395
+ {
10396
+ "BearerAuth": []
10397
+ }
10398
+ ],
10399
+ "parameters": [
10400
+ {
10401
+ "in": "path",
10402
+ "name": "appId",
10403
+ "required": true,
10404
+ "schema": {
10405
+ "type": "string",
10406
+ "format": "uuid"
10407
+ }
10408
+ },
10409
+ {
10410
+ "in": "path",
10411
+ "name": "template_id",
10412
+ "required": true,
10413
+ "schema": {
10414
+ "type": "string",
10415
+ "format": "uuid"
10416
+ }
10417
+ },
10418
+ {
10419
+ "in": "query",
10420
+ "name": "limit",
10421
+ "schema": {
10422
+ "type": "integer",
10423
+ "default": 50,
10424
+ "minimum": 1,
10425
+ "maximum": 500
10426
+ }
10427
+ },
10428
+ {
10429
+ "in": "query",
10430
+ "name": "offset",
10431
+ "schema": {
10432
+ "type": "integer",
10433
+ "default": 0,
10434
+ "minimum": 0
10435
+ }
10436
+ }
10437
+ ],
10340
10438
  "responses": {
10341
10439
  "200": {
10342
- "description": "Resolved template spec",
10440
+ "description": "Agents in the fleet",
10343
10441
  "content": {
10344
10442
  "application/json": {
10345
10443
  "schema": {
10346
- "$ref": "#/components/schemas/TemplatePreviewResponse"
10444
+ "$ref": "#/components/schemas/ListFleetAgentsResponse"
10347
10445
  }
10348
10446
  }
10349
10447
  }
@@ -10354,108 +10452,178 @@
10354
10452
  }
10355
10453
  }
10356
10454
  },
10357
- "/v1/platform/users/upsert": {
10455
+ "/v1/platform/apps/{appId}/fleets/{template_id}/bulk-patch": {
10358
10456
  "post": {
10359
10457
  "tags": [
10360
10458
  "Platform"
10361
10459
  ],
10362
- "summary": "Provision or look up a platform user",
10363
- "description": "Upserts a user using either an OIDC subject_token (verified against the platform app's JWKS) or an email address. Returns the user handle and connection ID.",
10460
+ "summary": "Patch every agent in a fleet",
10461
+ "description": "Applies one patch to every agent in the cohort. The field allowlist is deliberately narrower than a single-agent PATCH: guardrails and capability flags (intents_api_enabled, execution_intents_enabled) cannot be changed here, because at cohort scale nobody reviews the change per agent. Read the allowlist from bulk_patchable_fields on the fleet summary rather than hard-coding it. A field outside it returns 400 naming the field.\n",
10462
+ "operationId": "bulkPatchFleet",
10364
10463
  "security": [
10365
10464
  {
10366
10465
  "BearerAuth": []
10367
10466
  }
10368
10467
  ],
10468
+ "parameters": [
10469
+ {
10470
+ "in": "path",
10471
+ "name": "appId",
10472
+ "required": true,
10473
+ "schema": {
10474
+ "type": "string",
10475
+ "format": "uuid"
10476
+ }
10477
+ },
10478
+ {
10479
+ "in": "path",
10480
+ "name": "template_id",
10481
+ "required": true,
10482
+ "schema": {
10483
+ "type": "string",
10484
+ "format": "uuid"
10485
+ }
10486
+ }
10487
+ ],
10369
10488
  "requestBody": {
10370
10489
  "required": true,
10371
10490
  "content": {
10372
10491
  "application/json": {
10373
10492
  "schema": {
10374
- "$ref": "#/components/schemas/UpsertPlatformUserRequest"
10493
+ "type": "object",
10494
+ "required": [
10495
+ "patch"
10496
+ ],
10497
+ "properties": {
10498
+ "patch": {
10499
+ "type": "object",
10500
+ "additionalProperties": true,
10501
+ "description": "Fields to set on every agent in the cohort."
10502
+ }
10503
+ }
10375
10504
  }
10376
10505
  }
10377
10506
  }
10378
10507
  },
10379
10508
  "responses": {
10380
10509
  "200": {
10381
- "description": "Existing user found",
10510
+ "description": "Patch applied",
10382
10511
  "content": {
10383
10512
  "application/json": {
10384
10513
  "schema": {
10385
- "$ref": "#/components/schemas/PlatformUserResponse"
10514
+ "type": "object",
10515
+ "properties": {
10516
+ "fields_applied": {
10517
+ "type": "array",
10518
+ "items": {
10519
+ "type": "string"
10520
+ }
10521
+ },
10522
+ "agents_matched": {
10523
+ "type": "integer",
10524
+ "format": "int64"
10525
+ },
10526
+ "agents_updated": {
10527
+ "type": "integer",
10528
+ "format": "int64"
10529
+ }
10530
+ }
10386
10531
  }
10387
10532
  }
10388
10533
  }
10389
10534
  },
10390
- "201": {
10391
- "description": "New user created",
10392
- "content": {
10393
- "application/json": {
10394
- "schema": {
10395
- "$ref": "#/components/schemas/PlatformUserResponse"
10396
- }
10397
- }
10398
- }
10535
+ "400": {
10536
+ "description": "A field outside the bulk-patch allowlist, or an empty patch"
10399
10537
  },
10400
- "409": {
10401
- "description": "User exists in a different organization. Contains a link_required payload with an OAuth authorize URL for cross-org consent.",
10402
- "content": {
10403
- "application/json": {
10404
- "schema": {
10405
- "$ref": "#/components/schemas/PlatformUserLinkRequiredResponse"
10406
- }
10407
- }
10408
- }
10538
+ "404": {
10539
+ "$ref": "#/components/responses/NotFound"
10409
10540
  }
10410
10541
  }
10411
10542
  }
10412
10543
  },
10413
- "/v1/platform/siwe/challenge": {
10544
+ "/v1/platform/apps/{appId}/fleets/{template_id}/rollout": {
10414
10545
  "post": {
10415
10546
  "tags": [
10416
10547
  "Platform"
10417
10548
  ],
10418
- "summary": "Issue SIWE nonce",
10419
- "description": "Creates a one-time nonce for Sign-In With Ethereum user provisioning.\nRequires platform (`plt_`) authentication. The nonce expires in 5 minutes.\n",
10549
+ "summary": "Roll the current template version out to its fleet",
10550
+ "description": "Brings every agent in the cohort up to the template's current version. An agent changed outside fleet control is skipped rather than corrected, and the fields that caused the skip are recorded on it. force=true overrides that skip but still cannot carry a guardrail or a capability flag. dry_run=true reports what would happen and claims nothing, so it never blocks a real rollout. Only one rollout may run per template at a time; a second returns 409.\n",
10551
+ "operationId": "rolloutFleet",
10420
10552
  "security": [
10421
10553
  {
10422
10554
  "BearerAuth": []
10423
10555
  }
10424
10556
  ],
10557
+ "parameters": [
10558
+ {
10559
+ "in": "path",
10560
+ "name": "appId",
10561
+ "required": true,
10562
+ "schema": {
10563
+ "type": "string",
10564
+ "format": "uuid"
10565
+ }
10566
+ },
10567
+ {
10568
+ "in": "path",
10569
+ "name": "template_id",
10570
+ "required": true,
10571
+ "schema": {
10572
+ "type": "string",
10573
+ "format": "uuid"
10574
+ }
10575
+ }
10576
+ ],
10425
10577
  "requestBody": {
10426
10578
  "required": false,
10427
10579
  "content": {
10428
10580
  "application/json": {
10429
10581
  "schema": {
10430
- "$ref": "#/components/schemas/SiweChallengeRequest"
10582
+ "type": "object",
10583
+ "properties": {
10584
+ "force": {
10585
+ "type": "boolean",
10586
+ "default": false,
10587
+ "description": "Overwrite hand edits. Cannot carry guardrails."
10588
+ },
10589
+ "dry_run": {
10590
+ "type": "boolean",
10591
+ "default": false,
10592
+ "description": "Report the plan without applying it."
10593
+ }
10594
+ }
10431
10595
  }
10432
10596
  }
10433
10597
  }
10434
10598
  },
10435
10599
  "responses": {
10436
10600
  "200": {
10437
- "description": "Nonce issued",
10601
+ "description": "Rollout result",
10438
10602
  "content": {
10439
10603
  "application/json": {
10440
10604
  "schema": {
10441
- "$ref": "#/components/schemas/SiweChallengeResponse"
10605
+ "$ref": "#/components/schemas/FleetRolloutResponse"
10442
10606
  }
10443
10607
  }
10444
10608
  }
10445
10609
  },
10446
- "400": {
10447
- "description": "SIWE domain not configured"
10610
+ "404": {
10611
+ "$ref": "#/components/responses/NotFound"
10612
+ },
10613
+ "409": {
10614
+ "description": "A rollout is already running for this template"
10448
10615
  }
10449
10616
  }
10450
10617
  }
10451
10618
  },
10452
- "/v1/platform/apps/{appId}/users": {
10453
- "get": {
10619
+ "/v1/platform/apps/{appId}/fleets/{template_id}/pause": {
10620
+ "post": {
10454
10621
  "tags": [
10455
10622
  "Platform"
10456
10623
  ],
10457
- "summary": "List connected users",
10458
- "description": "List all users connected to this platform app.",
10624
+ "summary": "Deactivate every agent in a fleet",
10625
+ "description": "Sets is_active=false on the whole cohort. The blast radius is the point: this exists for the moment an operator needs a thousand agents to stop at once.\n",
10626
+ "operationId": "pauseFleet",
10459
10627
  "security": [
10460
10628
  {
10461
10629
  "BearerAuth": []
@@ -10470,40 +10638,47 @@
10470
10638
  "type": "string",
10471
10639
  "format": "uuid"
10472
10640
  }
10641
+ },
10642
+ {
10643
+ "in": "path",
10644
+ "name": "template_id",
10645
+ "required": true,
10646
+ "schema": {
10647
+ "type": "string",
10648
+ "format": "uuid"
10649
+ }
10473
10650
  }
10474
10651
  ],
10475
10652
  "responses": {
10476
10653
  "200": {
10477
- "description": "Connected users",
10654
+ "description": "Agents paused",
10478
10655
  "content": {
10479
10656
  "application/json": {
10480
10657
  "schema": {
10481
10658
  "type": "object",
10482
- "required": [
10483
- "users"
10484
- ],
10485
10659
  "properties": {
10486
- "users": {
10487
- "type": "array",
10488
- "items": {
10489
- "$ref": "#/components/schemas/PlatformConnectedUserResponse"
10490
- }
10660
+ "agents_paused": {
10661
+ "type": "integer",
10662
+ "format": "int64"
10491
10663
  }
10492
10664
  }
10493
10665
  }
10494
10666
  }
10495
10667
  }
10668
+ },
10669
+ "404": {
10670
+ "$ref": "#/components/responses/NotFound"
10496
10671
  }
10497
10672
  }
10498
10673
  }
10499
10674
  },
10500
- "/v1/platform/connections/{connectionId}/bootstrap": {
10675
+ "/v1/platform/apps/{appId}/templates/{template_id}/preview": {
10501
10676
  "post": {
10502
10677
  "tags": [
10503
10678
  "Platform"
10504
10679
  ],
10505
- "summary": "Bootstrap resources for a connected user",
10506
- "description": "Executes a template to create vault, agent, and policies for the connected user. Returns a claim URL and token for the user to claim their resources.",
10680
+ "summary": "Preview resolved template spec",
10681
+ "description": "Resolves `{{params.*}}` and `{{subject.*}}` placeholders in a template spec\nwithout provisioning resources. Useful for validating parameterized bootstrap templates.\n",
10507
10682
  "security": [
10508
10683
  {
10509
10684
  "BearerAuth": []
@@ -10512,7 +10687,16 @@
10512
10687
  "parameters": [
10513
10688
  {
10514
10689
  "in": "path",
10515
- "name": "connectionId",
10690
+ "name": "appId",
10691
+ "required": true,
10692
+ "schema": {
10693
+ "type": "string",
10694
+ "format": "uuid"
10695
+ }
10696
+ },
10697
+ {
10698
+ "in": "path",
10699
+ "name": "template_id",
10516
10700
  "required": true,
10517
10701
  "schema": {
10518
10702
  "type": "string",
@@ -10521,26 +10705,219 @@
10521
10705
  }
10522
10706
  ],
10523
10707
  "requestBody": {
10524
- "required": true,
10708
+ "required": false,
10525
10709
  "content": {
10526
10710
  "application/json": {
10527
10711
  "schema": {
10528
- "$ref": "#/components/schemas/BootstrapRequest"
10712
+ "$ref": "#/components/schemas/TemplatePreviewRequest"
10529
10713
  }
10530
10714
  }
10531
10715
  }
10532
10716
  },
10533
10717
  "responses": {
10534
- "201": {
10535
- "description": "Resources bootstrapped",
10718
+ "200": {
10719
+ "description": "Resolved template spec",
10536
10720
  "content": {
10537
10721
  "application/json": {
10538
10722
  "schema": {
10539
- "$ref": "#/components/schemas/BootstrapResponse"
10723
+ "$ref": "#/components/schemas/TemplatePreviewResponse"
10540
10724
  }
10541
10725
  }
10542
10726
  }
10543
- }
10727
+ },
10728
+ "404": {
10729
+ "$ref": "#/components/responses/NotFound"
10730
+ }
10731
+ }
10732
+ }
10733
+ },
10734
+ "/v1/platform/users/upsert": {
10735
+ "post": {
10736
+ "tags": [
10737
+ "Platform"
10738
+ ],
10739
+ "summary": "Provision or look up a platform user",
10740
+ "description": "Upserts a user using either an OIDC subject_token (verified against the platform app's JWKS) or an email address. Returns the user handle and connection ID.",
10741
+ "security": [
10742
+ {
10743
+ "BearerAuth": []
10744
+ }
10745
+ ],
10746
+ "requestBody": {
10747
+ "required": true,
10748
+ "content": {
10749
+ "application/json": {
10750
+ "schema": {
10751
+ "$ref": "#/components/schemas/UpsertPlatformUserRequest"
10752
+ }
10753
+ }
10754
+ }
10755
+ },
10756
+ "responses": {
10757
+ "200": {
10758
+ "description": "Existing user found",
10759
+ "content": {
10760
+ "application/json": {
10761
+ "schema": {
10762
+ "$ref": "#/components/schemas/PlatformUserResponse"
10763
+ }
10764
+ }
10765
+ }
10766
+ },
10767
+ "201": {
10768
+ "description": "New user created",
10769
+ "content": {
10770
+ "application/json": {
10771
+ "schema": {
10772
+ "$ref": "#/components/schemas/PlatformUserResponse"
10773
+ }
10774
+ }
10775
+ }
10776
+ },
10777
+ "409": {
10778
+ "description": "User exists in a different organization. Contains a link_required payload with an OAuth authorize URL for cross-org consent.",
10779
+ "content": {
10780
+ "application/json": {
10781
+ "schema": {
10782
+ "$ref": "#/components/schemas/PlatformUserLinkRequiredResponse"
10783
+ }
10784
+ }
10785
+ }
10786
+ }
10787
+ }
10788
+ }
10789
+ },
10790
+ "/v1/platform/siwe/challenge": {
10791
+ "post": {
10792
+ "tags": [
10793
+ "Platform"
10794
+ ],
10795
+ "summary": "Issue SIWE nonce",
10796
+ "description": "Creates a one-time nonce for Sign-In With Ethereum user provisioning.\nRequires platform (`plt_`) authentication. The nonce expires in 5 minutes.\n",
10797
+ "security": [
10798
+ {
10799
+ "BearerAuth": []
10800
+ }
10801
+ ],
10802
+ "requestBody": {
10803
+ "required": false,
10804
+ "content": {
10805
+ "application/json": {
10806
+ "schema": {
10807
+ "$ref": "#/components/schemas/SiweChallengeRequest"
10808
+ }
10809
+ }
10810
+ }
10811
+ },
10812
+ "responses": {
10813
+ "200": {
10814
+ "description": "Nonce issued",
10815
+ "content": {
10816
+ "application/json": {
10817
+ "schema": {
10818
+ "$ref": "#/components/schemas/SiweChallengeResponse"
10819
+ }
10820
+ }
10821
+ }
10822
+ },
10823
+ "400": {
10824
+ "description": "SIWE domain not configured"
10825
+ }
10826
+ }
10827
+ }
10828
+ },
10829
+ "/v1/platform/apps/{appId}/users": {
10830
+ "get": {
10831
+ "tags": [
10832
+ "Platform"
10833
+ ],
10834
+ "summary": "List connected users",
10835
+ "description": "List all users connected to this platform app.",
10836
+ "security": [
10837
+ {
10838
+ "BearerAuth": []
10839
+ }
10840
+ ],
10841
+ "parameters": [
10842
+ {
10843
+ "in": "path",
10844
+ "name": "appId",
10845
+ "required": true,
10846
+ "schema": {
10847
+ "type": "string",
10848
+ "format": "uuid"
10849
+ }
10850
+ }
10851
+ ],
10852
+ "responses": {
10853
+ "200": {
10854
+ "description": "Connected users",
10855
+ "content": {
10856
+ "application/json": {
10857
+ "schema": {
10858
+ "type": "object",
10859
+ "required": [
10860
+ "users"
10861
+ ],
10862
+ "properties": {
10863
+ "users": {
10864
+ "type": "array",
10865
+ "items": {
10866
+ "$ref": "#/components/schemas/PlatformConnectedUserResponse"
10867
+ }
10868
+ }
10869
+ }
10870
+ }
10871
+ }
10872
+ }
10873
+ }
10874
+ }
10875
+ }
10876
+ },
10877
+ "/v1/platform/connections/{connectionId}/bootstrap": {
10878
+ "post": {
10879
+ "tags": [
10880
+ "Platform"
10881
+ ],
10882
+ "summary": "Bootstrap resources for a connected user",
10883
+ "description": "Executes a template to create vault, agent, and policies for the connected user. Returns a claim URL and token for the user to claim their resources.",
10884
+ "security": [
10885
+ {
10886
+ "BearerAuth": []
10887
+ }
10888
+ ],
10889
+ "parameters": [
10890
+ {
10891
+ "in": "path",
10892
+ "name": "connectionId",
10893
+ "required": true,
10894
+ "schema": {
10895
+ "type": "string",
10896
+ "format": "uuid"
10897
+ }
10898
+ }
10899
+ ],
10900
+ "requestBody": {
10901
+ "required": true,
10902
+ "content": {
10903
+ "application/json": {
10904
+ "schema": {
10905
+ "$ref": "#/components/schemas/BootstrapRequest"
10906
+ }
10907
+ }
10908
+ }
10909
+ },
10910
+ "responses": {
10911
+ "201": {
10912
+ "description": "Resources bootstrapped",
10913
+ "content": {
10914
+ "application/json": {
10915
+ "schema": {
10916
+ "$ref": "#/components/schemas/BootstrapResponse"
10917
+ }
10918
+ }
10919
+ }
10920
+ }
10544
10921
  }
10545
10922
  }
10546
10923
  },
@@ -11651,6 +12028,40 @@
11651
12028
  "$ref": "#/components/responses/NotFound"
11652
12029
  }
11653
12030
  }
12031
+ },
12032
+ "post": {
12033
+ "tags": [
12034
+ "Platform"
12035
+ ],
12036
+ "summary": "Create an approval on a platform connection",
12037
+ "operationId": "createConnectionApproval",
12038
+ "security": [
12039
+ {
12040
+ "BearerAuth": []
12041
+ }
12042
+ ],
12043
+ "parameters": [
12044
+ {
12045
+ "in": "path",
12046
+ "name": "connectionId",
12047
+ "required": true,
12048
+ "schema": {
12049
+ "type": "string",
12050
+ "format": "uuid"
12051
+ }
12052
+ }
12053
+ ],
12054
+ "responses": {
12055
+ "201": {
12056
+ "description": "Approval created"
12057
+ },
12058
+ "403": {
12059
+ "$ref": "#/components/responses/Forbidden"
12060
+ },
12061
+ "404": {
12062
+ "$ref": "#/components/responses/NotFound"
12063
+ }
12064
+ }
11654
12065
  }
11655
12066
  },
11656
12067
  "/v1/platform/connections/{connectionId}/approvals/{approvalId}": {
@@ -12250,16 +12661,13 @@
12250
12661
  "$ref": "#/components/responses/NotFound"
12251
12662
  }
12252
12663
  }
12253
- }
12254
- },
12255
- "/v1/platform/connections/{connectionId}/portfolio": {
12256
- "get": {
12664
+ },
12665
+ "post": {
12257
12666
  "tags": [
12258
12667
  "Platform"
12259
12668
  ],
12260
- "summary": "Portfolio for connection agents",
12261
- "description": "Aggregates signing-key and smart-account balances for agents provisioned on\nthe connection. plt_ auth only.\n",
12262
- "operationId": "getConnectionPortfolio",
12669
+ "summary": "Alias for PATCH on this path — same handler, same limited settings. Present because some clients cannot send PATCH.",
12670
+ "operationId": "postConnectionAgent",
12263
12671
  "security": [
12264
12672
  {
12265
12673
  "BearerAuth": []
@@ -12276,31 +12684,77 @@
12276
12684
  }
12277
12685
  },
12278
12686
  {
12279
- "in": "query",
12280
- "name": "chains",
12281
- "required": false,
12687
+ "in": "path",
12688
+ "name": "agentId",
12689
+ "required": true,
12282
12690
  "schema": {
12283
12691
  "type": "string",
12284
- "description": "Comma-separated chain filter (e.g. `ethereum,base`)"
12285
- }
12286
- },
12287
- {
12288
- "in": "query",
12289
- "name": "include_tokens",
12290
- "required": false,
12291
- "schema": {
12292
- "type": "boolean"
12692
+ "format": "uuid"
12293
12693
  }
12294
12694
  }
12295
12695
  ],
12296
12696
  "responses": {
12297
12697
  "200": {
12298
- "description": "Connection-scoped portfolio",
12299
- "content": {
12300
- "application/json": {
12301
- "schema": {
12302
- "$ref": "#/components/schemas/PortfolioResponse"
12303
- }
12698
+ "description": "Agent updated"
12699
+ },
12700
+ "403": {
12701
+ "$ref": "#/components/responses/Forbidden"
12702
+ },
12703
+ "404": {
12704
+ "$ref": "#/components/responses/NotFound"
12705
+ }
12706
+ }
12707
+ }
12708
+ },
12709
+ "/v1/platform/connections/{connectionId}/portfolio": {
12710
+ "get": {
12711
+ "tags": [
12712
+ "Platform"
12713
+ ],
12714
+ "summary": "Portfolio for connection agents",
12715
+ "description": "Aggregates signing-key and smart-account balances for agents provisioned on\nthe connection. plt_ auth only.\n",
12716
+ "operationId": "getConnectionPortfolio",
12717
+ "security": [
12718
+ {
12719
+ "BearerAuth": []
12720
+ }
12721
+ ],
12722
+ "parameters": [
12723
+ {
12724
+ "in": "path",
12725
+ "name": "connectionId",
12726
+ "required": true,
12727
+ "schema": {
12728
+ "type": "string",
12729
+ "format": "uuid"
12730
+ }
12731
+ },
12732
+ {
12733
+ "in": "query",
12734
+ "name": "chains",
12735
+ "required": false,
12736
+ "schema": {
12737
+ "type": "string",
12738
+ "description": "Comma-separated chain filter (e.g. `ethereum,base`)"
12739
+ }
12740
+ },
12741
+ {
12742
+ "in": "query",
12743
+ "name": "include_tokens",
12744
+ "required": false,
12745
+ "schema": {
12746
+ "type": "boolean"
12747
+ }
12748
+ }
12749
+ ],
12750
+ "responses": {
12751
+ "200": {
12752
+ "description": "Connection-scoped portfolio",
12753
+ "content": {
12754
+ "application/json": {
12755
+ "schema": {
12756
+ "$ref": "#/components/schemas/PortfolioResponse"
12757
+ }
12304
12758
  }
12305
12759
  }
12306
12760
  },
@@ -13426,6 +13880,50 @@
13426
13880
  "$ref": "#/components/responses/Conflict"
13427
13881
  }
13428
13882
  }
13883
+ },
13884
+ "post": {
13885
+ "tags": [
13886
+ "Approvals"
13887
+ ],
13888
+ "summary": "One-click approve or deny, as an API call. The GET on this path is the preview an email link opens; this is the decision itself.",
13889
+ "operationId": "quickDecide",
13890
+ "requestBody": {
13891
+ "required": true,
13892
+ "content": {
13893
+ "application/json": {
13894
+ "schema": {
13895
+ "type": "object",
13896
+ "required": [
13897
+ "token",
13898
+ "decision"
13899
+ ],
13900
+ "properties": {
13901
+ "token": {
13902
+ "type": "string"
13903
+ },
13904
+ "decision": {
13905
+ "type": "string",
13906
+ "enum": [
13907
+ "approve",
13908
+ "deny"
13909
+ ]
13910
+ }
13911
+ }
13912
+ }
13913
+ }
13914
+ }
13915
+ },
13916
+ "responses": {
13917
+ "200": {
13918
+ "description": "Decision recorded"
13919
+ },
13920
+ "400": {
13921
+ "description": "Malformed or expired token"
13922
+ },
13923
+ "404": {
13924
+ "$ref": "#/components/responses/NotFound"
13925
+ }
13926
+ }
13429
13927
  }
13430
13928
  },
13431
13929
  "/v1/deposit-destinations": {
@@ -16317,6 +16815,16 @@
16317
16815
  },
16318
16816
  "404": {
16319
16817
  "$ref": "#/components/responses/NotFound"
16818
+ },
16819
+ "503": {
16820
+ "description": "The runtime's last start failed less than 120 seconds ago and is not\nbeing retried yet. Chat starts a stopped runtime, so a client that\nretries on failure turns every attempt into another deploy — one\npolling client did exactly that every 8 seconds for days. Wait out\nthe cooldown, or call the start endpoint to see the underlying error.\n",
16821
+ "content": {
16822
+ "application/json": {
16823
+ "schema": {
16824
+ "$ref": "#/components/schemas/ProblemDetails"
16825
+ }
16826
+ }
16827
+ }
16320
16828
  }
16321
16829
  }
16322
16830
  }
@@ -16391,6 +16899,37 @@
16391
16899
  "$ref": "#/components/responses/NotFound"
16392
16900
  }
16393
16901
  }
16902
+ },
16903
+ "delete": {
16904
+ "tags": [
16905
+ "Agent Memory"
16906
+ ],
16907
+ "summary": "Delete a memory namespace and every entry in it. Destructive and not reversible; there is no per-entry confirmation.",
16908
+ "operationId": "deleteMemoryNamespace",
16909
+ "parameters": [
16910
+ {
16911
+ "$ref": "#/components/parameters/AgentId"
16912
+ },
16913
+ {
16914
+ "name": "namespace",
16915
+ "in": "path",
16916
+ "required": true,
16917
+ "schema": {
16918
+ "type": "string"
16919
+ }
16920
+ }
16921
+ ],
16922
+ "responses": {
16923
+ "204": {
16924
+ "description": "Namespace deleted"
16925
+ },
16926
+ "401": {
16927
+ "$ref": "#/components/responses/Unauthorized"
16928
+ },
16929
+ "404": {
16930
+ "$ref": "#/components/responses/NotFound"
16931
+ }
16932
+ }
16394
16933
  }
16395
16934
  },
16396
16935
  "/v1/agents/{agent_id}/memory/{namespace}/{key}": {
@@ -17444,95 +17983,168 @@
17444
17983
  }
17445
17984
  }
17446
17985
  },
17447
- "/v1/webhooks/discord/{webhook_path}": {
17448
- "post": {
17986
+ "/v1/platform/apps/{app_id}/usage": {
17987
+ "get": {
17449
17988
  "tags": [
17450
- "Agent Channels"
17989
+ "Platform API"
17451
17990
  ],
17452
- "summary": "Discord webhook",
17453
- "description": "Public webhook endpoint for receiving Discord bot interactions.",
17454
- "operationId": "discordWebhook",
17455
- "security": [],
17991
+ "summary": "Usage for every connection on an app",
17992
+ "description": "Billable activity for the current month, grouped by end-user connection,\n**plus what could not be charged to one**.\n\nThe `unattributed` block is not an implementation detail. Summing only the\nper-connection numbers gives a figure that will not match the invoice you\nare reconciling against, and the gap is usage that belongs to a real\nend-user we cannot name.\n\nTwo kinds, deliberately kept apart:\n\n* `none` — no platform linkage at all. Normal for most traffic, not a problem.\n* `ambiguous` — the agent belongs to several connections and no\n `X-Platform-Connection` header said which. This usage belongs to\n *someone*. `has_ambiguous_usage` flags it so you do not have to notice\n a non-zero nested number.\n\n`totals` is derived from the parts, never queried separately, so it cannot\ndisagree with its own breakdown.\n",
17993
+ "operationId": "getAppUsage",
17456
17994
  "parameters": [
17457
17995
  {
17458
- "name": "webhook_path",
17996
+ "name": "app_id",
17459
17997
  "in": "path",
17460
17998
  "required": true,
17461
17999
  "schema": {
17462
- "type": "string"
18000
+ "type": "string",
18001
+ "format": "uuid"
17463
18002
  }
17464
18003
  }
17465
18004
  ],
17466
- "requestBody": {
17467
- "required": true,
17468
- "content": {
17469
- "application/json": {
17470
- "schema": {
17471
- "type": "object"
17472
- }
17473
- }
17474
- }
17475
- },
17476
18005
  "responses": {
17477
18006
  "200": {
17478
- "description": "Webhook processed"
18007
+ "description": "Usage report",
18008
+ "content": {
18009
+ "application/json": {
18010
+ "schema": {
18011
+ "$ref": "#/components/schemas/AppUsageReport"
18012
+ }
18013
+ }
18014
+ }
18015
+ },
18016
+ "404": {
18017
+ "$ref": "#/components/responses/NotFound"
17479
18018
  }
17480
18019
  }
17481
18020
  }
17482
18021
  },
17483
- "/v1/oauth/providers": {
18022
+ "/v1/platform/apps/{app_id}/usage/export": {
17484
18023
  "get": {
17485
18024
  "tags": [
17486
- "OAuth Connect"
18025
+ "Platform API"
18026
+ ],
18027
+ "summary": "Usage as CSV for billing reconciliation",
18028
+ "description": "The same report as `GET /usage`, as CSV. Includes the `ambiguous`, `none`\nand `total` rows — a CSV listing only connections looks complete and is\nnot, and whoever imports it has no way to tell.\n",
18029
+ "operationId": "exportAppUsage",
18030
+ "parameters": [
18031
+ {
18032
+ "name": "app_id",
18033
+ "in": "path",
18034
+ "required": true,
18035
+ "schema": {
18036
+ "type": "string",
18037
+ "format": "uuid"
18038
+ }
18039
+ }
17487
18040
  ],
17488
- "summary": "List OAuth providers",
17489
- "description": "Returns the list of supported OAuth providers with their metadata,\navailable scopes, and authorization URLs. No authentication required.\n",
17490
- "operationId": "listOAuthProviders",
17491
- "security": [],
17492
18041
  "responses": {
17493
18042
  "200": {
17494
- "description": "Provider list",
18043
+ "description": "CSV",
17495
18044
  "content": {
17496
- "application/json": {
18045
+ "text/csv": {
17497
18046
  "schema": {
17498
- "$ref": "#/components/schemas/OAuthProviderListResponse"
18047
+ "type": "string"
17499
18048
  }
17500
18049
  }
17501
18050
  }
18051
+ },
18052
+ "404": {
18053
+ "$ref": "#/components/responses/NotFound"
17502
18054
  }
17503
18055
  }
17504
18056
  }
17505
18057
  },
17506
- "/v1/agents/{agent_id}/oauth/connect": {
18058
+ "/v1/org/apply": {
17507
18059
  "post": {
17508
18060
  "tags": [
17509
- "OAuth Connect"
17510
- ],
17511
- "summary": "Initiate OAuth connection",
17512
- "description": "Start an OAuth authorization flow for the specified agent and provider.\nReturns the authorization URL to redirect the user to. Human-only.\n",
17513
- "operationId": "connectOAuth",
17514
- "parameters": [
17515
- {
17516
- "$ref": "#/components/parameters/AgentId"
17517
- }
18061
+ "Organization"
17518
18062
  ],
18063
+ "summary": "Apply a chart",
18064
+ "description": "Create what the chart describes. Human users only.\n\n**Apply calls the same handlers the HTTP routes call.** Creating a vault\nruns five gates before anything is written — the delegation scope, a name\ncheck, control-plane consensus, a creation rate limit and the tier quota\n— and creating an agent runs its own. A reconciler that wrote through the\nrepositories would skip all of them and would look, in review, exactly\nlike one that did not.\n\nSo if your org has consensus configured on `vault.create`, applying a\nchart queues an approval exactly as a dashboard click would. That resource\ncomes back as `awaiting_approval` rather than failing the whole chart.\n\nPer-resource results: `created`, `unchanged`, `skipped`, `refused`,\n`awaiting_approval`, `failed`. `needs_attention` is true when the chart is\nnot fully applied — something is waiting on a person, whether an approval,\nan OAuth sign-in, or a resource that drifted and was left alone.\n\nSave `applied_state` to `.1claw/apply-state.json`. It records what apply\nset, which is what lets the next run tell drift from a first apply.\n\nv1 creates and reports; it does not delete, prune, or patch in place. An\napply that silently deletes is an apply nobody runs twice.\n",
18065
+ "operationId": "applyChart",
17519
18066
  "requestBody": {
17520
18067
  "required": true,
17521
18068
  "content": {
17522
18069
  "application/json": {
17523
18070
  "schema": {
17524
- "$ref": "#/components/schemas/ConnectOAuthRequest"
18071
+ "type": "object",
18072
+ "required": [
18073
+ "chart"
18074
+ ],
18075
+ "properties": {
18076
+ "chart": {
18077
+ "type": "object",
18078
+ "description": "A chart document — `apiVersion`, `kind`, `metadata`, `spec`."
18079
+ },
18080
+ "applied_state": {
18081
+ "type": "object",
18082
+ "additionalProperties": true
18083
+ }
18084
+ }
17525
18085
  }
17526
18086
  }
17527
18087
  }
17528
18088
  },
17529
18089
  "responses": {
17530
18090
  "200": {
17531
- "description": "Authorization URL generated",
18091
+ "description": "What happened to each resource",
17532
18092
  "content": {
17533
18093
  "application/json": {
17534
18094
  "schema": {
17535
- "$ref": "#/components/schemas/ConnectOAuthResponse"
18095
+ "type": "object",
18096
+ "properties": {
18097
+ "chart_name": {
18098
+ "type": "string"
18099
+ },
18100
+ "resources": {
18101
+ "type": "array",
18102
+ "items": {
18103
+ "type": "object",
18104
+ "properties": {
18105
+ "kind": {
18106
+ "type": "string"
18107
+ },
18108
+ "name": {
18109
+ "type": "string"
18110
+ },
18111
+ "result": {
18112
+ "type": "string",
18113
+ "enum": [
18114
+ "created",
18115
+ "unchanged",
18116
+ "skipped",
18117
+ "refused",
18118
+ "awaiting_approval",
18119
+ "failed"
18120
+ ]
18121
+ },
18122
+ "id": {
18123
+ "type": "string",
18124
+ "format": "uuid"
18125
+ },
18126
+ "detail": {
18127
+ "type": "string"
18128
+ }
18129
+ }
18130
+ }
18131
+ },
18132
+ "warnings": {
18133
+ "type": "array",
18134
+ "items": {
18135
+ "type": "string"
18136
+ }
18137
+ },
18138
+ "applied_state": {
18139
+ "type": "object",
18140
+ "additionalProperties": true,
18141
+ "description": "Save to `.1claw/apply-state.json`."
18142
+ },
18143
+ "needs_attention": {
18144
+ "type": "boolean",
18145
+ "description": "The chart is not fully applied — something is waiting on a person."
18146
+ }
18147
+ }
17536
18148
  }
17537
18149
  }
17538
18150
  }
@@ -17542,19 +18154,1896 @@
17542
18154
  },
17543
18155
  "403": {
17544
18156
  "$ref": "#/components/responses/Forbidden"
17545
- },
17546
- "404": {
17547
- "$ref": "#/components/responses/NotFound"
17548
18157
  }
17549
18158
  }
17550
18159
  }
17551
18160
  },
17552
- "/v1/agents/{agent_id}/oauth/connections": {
18161
+ "/v1/org/approval-learning/shadow-report": {
17553
18162
  "get": {
17554
18163
  "tags": [
17555
- "OAuth Connect"
18164
+ "Organization"
17556
18165
  ],
17557
- "summary": "List OAuth connections",
18166
+ "summary": "What would have been approved automatically",
18167
+ "description": "Every approval decision is observed, per **fingerprint bucket** — the\naction, the amount band, whether the recipient was new, and who they\nwere. This reports the buckets a person has approved without exception,\nand what promoting one would write into an agent's policy.\n\nObserving is not acting. In the default `shadow` mode nothing changes who\ngets asked; `can_promote` is false and the promote endpoint refuses.\n\nEach suggestion carries `would_write_rule` — the actual rule, built by the\nsame function promotion uses, so the report cannot promise something\npromotion would refuse.\n",
18168
+ "operationId": "getApprovalLearningShadowReport",
18169
+ "responses": {
18170
+ "200": {
18171
+ "description": "Report",
18172
+ "content": {
18173
+ "application/json": {
18174
+ "schema": {
18175
+ "type": "object",
18176
+ "properties": {
18177
+ "mode": {
18178
+ "type": "string",
18179
+ "enum": [
18180
+ "shadow",
18181
+ "enforce"
18182
+ ]
18183
+ },
18184
+ "threshold": {
18185
+ "type": "integer",
18186
+ "description": "Consecutive approvals in one bucket before it appears here."
18187
+ },
18188
+ "can_promote": {
18189
+ "type": "boolean"
18190
+ },
18191
+ "observed_buckets": {
18192
+ "type": "integer"
18193
+ },
18194
+ "total_decisions": {
18195
+ "type": "integer"
18196
+ },
18197
+ "suggestions": {
18198
+ "type": "array",
18199
+ "items": {
18200
+ "type": "object",
18201
+ "properties": {
18202
+ "profile_id": {
18203
+ "type": "string",
18204
+ "format": "uuid"
18205
+ },
18206
+ "action_type": {
18207
+ "type": "string"
18208
+ },
18209
+ "fingerprint_bucket": {
18210
+ "type": "string",
18211
+ "example": "refund.create|0-10|known|a@b.co"
18212
+ },
18213
+ "consecutive_approvals": {
18214
+ "type": "integer"
18215
+ },
18216
+ "total_requests": {
18217
+ "type": "integer"
18218
+ },
18219
+ "would_write_rule": {
18220
+ "type": "object",
18221
+ "nullable": true
18222
+ },
18223
+ "blocked": {
18224
+ "type": "string",
18225
+ "description": "Present when a bound prevents promotion, saying which."
18226
+ },
18227
+ "last_decision_at": {
18228
+ "type": "string",
18229
+ "format": "date-time"
18230
+ }
18231
+ }
18232
+ }
18233
+ }
18234
+ }
18235
+ }
18236
+ }
18237
+ }
18238
+ },
18239
+ "403": {
18240
+ "$ref": "#/components/responses/Forbidden"
18241
+ }
18242
+ }
18243
+ }
18244
+ },
18245
+ "/v1/org/approval-learning/{profile_id}/promote": {
18246
+ "post": {
18247
+ "tags": [
18248
+ "Organization"
18249
+ ],
18250
+ "summary": "Turn an observed pattern into a policy rule",
18251
+ "description": "Writes a rule into the named agent's `action_approval_policy`. Human users\nonly, and only when the organisation is in `enforce` mode.\n\n**The rule covers only what was actually approved.** Five approved $5\nrefunds to one customer produce `{ refund.create, asks above $10, that\nrecipient }` — a $49 request does not match it and still reaches a human.\n\nRefused when: fewer than five consecutive approvals; *any* past rejection\non the bucket (a long recent run must not hide a history of refusals); the\nbucket is for a recipient never paid before; the amount band has no upper\nedge; or the action grants or destroys authority.\n\n`widen_to_action_type` drops the amount and recipient constraints. It is\nnever the default and should be an explicit choice in your UI, not a\ncheckbox someone skims past.\n\nThe written rule is marked `promoted_from_learning` so an operator can\ntell it apart from one they wrote, and it replaces any existing rule for\nthe same action type — appending would leave two rules where only the\nfirst ever applies.\n",
18252
+ "operationId": "promoteApprovalLearningProfile",
18253
+ "parameters": [
18254
+ {
18255
+ "name": "profile_id",
18256
+ "in": "path",
18257
+ "required": true,
18258
+ "schema": {
18259
+ "type": "string",
18260
+ "format": "uuid"
18261
+ }
18262
+ }
18263
+ ],
18264
+ "requestBody": {
18265
+ "required": true,
18266
+ "content": {
18267
+ "application/json": {
18268
+ "schema": {
18269
+ "type": "object",
18270
+ "required": [
18271
+ "agent_id"
18272
+ ],
18273
+ "properties": {
18274
+ "agent_id": {
18275
+ "type": "string",
18276
+ "format": "uuid"
18277
+ },
18278
+ "widen_to_action_type": {
18279
+ "type": "boolean",
18280
+ "default": false
18281
+ }
18282
+ }
18283
+ }
18284
+ }
18285
+ }
18286
+ },
18287
+ "responses": {
18288
+ "200": {
18289
+ "description": "Promoted",
18290
+ "content": {
18291
+ "application/json": {
18292
+ "schema": {
18293
+ "type": "object",
18294
+ "properties": {
18295
+ "profile_id": {
18296
+ "type": "string",
18297
+ "format": "uuid"
18298
+ },
18299
+ "agent_id": {
18300
+ "type": "string",
18301
+ "format": "uuid"
18302
+ },
18303
+ "rule": {
18304
+ "type": "object"
18305
+ }
18306
+ }
18307
+ }
18308
+ }
18309
+ }
18310
+ },
18311
+ "400": {
18312
+ "$ref": "#/components/responses/BadRequest"
18313
+ },
18314
+ "403": {
18315
+ "$ref": "#/components/responses/Forbidden"
18316
+ },
18317
+ "404": {
18318
+ "$ref": "#/components/responses/NotFound"
18319
+ },
18320
+ "409": {
18321
+ "$ref": "#/components/responses/Conflict"
18322
+ }
18323
+ }
18324
+ }
18325
+ },
18326
+ "/v1/policy-presets": {
18327
+ "get": {
18328
+ "tags": [
18329
+ "Policies"
18330
+ ],
18331
+ "summary": "Named starting points for an agent's policy",
18332
+ "description": "Four presets an operator can choose between without reading a policy\ndocument. Public — a description of what 1Claw offers, not tenant data.\n\nEach carries a `headline`: the one-line consequence someone should read\nbefore choosing it, in the words they would use.\n",
18333
+ "operationId": "listPolicyPresets",
18334
+ "security": [],
18335
+ "responses": {
18336
+ "200": {
18337
+ "description": "Catalogue",
18338
+ "content": {
18339
+ "application/json": {
18340
+ "schema": {
18341
+ "type": "object",
18342
+ "properties": {
18343
+ "presets": {
18344
+ "type": "array",
18345
+ "items": {
18346
+ "type": "object"
18347
+ }
18348
+ }
18349
+ }
18350
+ }
18351
+ }
18352
+ }
18353
+ }
18354
+ }
18355
+ }
18356
+ },
18357
+ "/v1/agents/{agent_id}/policy-preset/preview": {
18358
+ "post": {
18359
+ "tags": [
18360
+ "Policies"
18361
+ ],
18362
+ "summary": "What a preset would change",
18363
+ "description": "Compiles a preset against this agent and reports which fields it would\n**widen** — loosen relative to what the agent can already do.\n\nWidening detection errs toward flagging: a false positive costs one extra\napproval, a false negative is a limit raised without review. Enabling a\ncapability widens; disabling does not. Raising a cap widens; setting a\nfirst cap does not, because absent means unlimited.\n",
18364
+ "operationId": "previewPolicyPreset",
18365
+ "parameters": [
18366
+ {
18367
+ "name": "agent_id",
18368
+ "in": "path",
18369
+ "required": true,
18370
+ "schema": {
18371
+ "type": "string",
18372
+ "format": "uuid"
18373
+ }
18374
+ }
18375
+ ],
18376
+ "requestBody": {
18377
+ "required": true,
18378
+ "content": {
18379
+ "application/json": {
18380
+ "schema": {
18381
+ "type": "object",
18382
+ "required": [
18383
+ "preset"
18384
+ ],
18385
+ "properties": {
18386
+ "preset": {
18387
+ "type": "string",
18388
+ "example": "small-business-spender"
18389
+ }
18390
+ }
18391
+ }
18392
+ }
18393
+ }
18394
+ },
18395
+ "responses": {
18396
+ "200": {
18397
+ "description": "Proposal",
18398
+ "content": {
18399
+ "application/json": {
18400
+ "schema": {
18401
+ "type": "object",
18402
+ "properties": {
18403
+ "preset_slug": {
18404
+ "type": "string"
18405
+ },
18406
+ "guardrails": {
18407
+ "type": "object"
18408
+ },
18409
+ "action_approval_policy": {
18410
+ "type": "object"
18411
+ },
18412
+ "access_policy": {
18413
+ "type": "object"
18414
+ },
18415
+ "widens": {
18416
+ "type": "array",
18417
+ "items": {
18418
+ "type": "string"
18419
+ },
18420
+ "description": "Fields this preset would loosen. Show these, not a generic warning."
18421
+ },
18422
+ "requires_guardrail_approval": {
18423
+ "type": "boolean"
18424
+ },
18425
+ "explanation": {
18426
+ "type": "string"
18427
+ }
18428
+ }
18429
+ }
18430
+ }
18431
+ }
18432
+ },
18433
+ "403": {
18434
+ "$ref": "#/components/responses/Forbidden"
18435
+ },
18436
+ "404": {
18437
+ "$ref": "#/components/responses/NotFound"
18438
+ }
18439
+ }
18440
+ }
18441
+ },
18442
+ "/v1/agents/{agent_id}/policy-preset": {
18443
+ "post": {
18444
+ "tags": [
18445
+ "Policies"
18446
+ ],
18447
+ "summary": "Apply a policy preset",
18448
+ "description": "**Applies through the agent update handler**, not by writing guardrail\ncolumns. A preset that wrote them directly would be a way around the\nguardrail widening approval flow wearing a friendlier interface.\n\nSo if the preset loosens something and your organisation gates that, you\nget the same **202 with a pending approval** you would get from editing\nthe agent by hand — not a quietly applied change. Pass `approval_id`\nonce that approval is granted.\n",
18449
+ "operationId": "applyPolicyPreset",
18450
+ "parameters": [
18451
+ {
18452
+ "name": "agent_id",
18453
+ "in": "path",
18454
+ "required": true,
18455
+ "schema": {
18456
+ "type": "string",
18457
+ "format": "uuid"
18458
+ }
18459
+ }
18460
+ ],
18461
+ "requestBody": {
18462
+ "required": true,
18463
+ "content": {
18464
+ "application/json": {
18465
+ "schema": {
18466
+ "type": "object",
18467
+ "required": [
18468
+ "preset"
18469
+ ],
18470
+ "properties": {
18471
+ "preset": {
18472
+ "type": "string"
18473
+ },
18474
+ "approval_id": {
18475
+ "type": "string",
18476
+ "format": "uuid"
18477
+ }
18478
+ }
18479
+ }
18480
+ }
18481
+ }
18482
+ },
18483
+ "responses": {
18484
+ "200": {
18485
+ "description": "Applied"
18486
+ },
18487
+ "202": {
18488
+ "description": "Queued behind a guardrail approval"
18489
+ },
18490
+ "403": {
18491
+ "$ref": "#/components/responses/Forbidden"
18492
+ },
18493
+ "404": {
18494
+ "$ref": "#/components/responses/NotFound"
18495
+ }
18496
+ }
18497
+ }
18498
+ },
18499
+ "/v1/agents/{agent_id}/trust": {
18500
+ "get": {
18501
+ "tags": [
18502
+ "Discovery"
18503
+ ],
18504
+ "summary": "What a listed agent has earned",
18505
+ "description": "Everything a publisher writes — name, description, tags — is a claim.\nThese are the parts they cannot write: whether a human reviewed the\nlisting, how many people installed it, what they rated it.\n\nPublic, because its purpose is to be read by someone deciding whether to\ninstall a stranger's agent. Only listed agents have public trust.\n\n**A listing with reports shows no badges at all.** \"Platform reviewed\"\nbeside an active complaint tells a reader the opposite of what they need.\nAn average rating appears only from three reviews — one rating is not an\naverage.\n",
18506
+ "operationId": "getAgentTrust",
18507
+ "security": [],
18508
+ "parameters": [
18509
+ {
18510
+ "name": "agent_id",
18511
+ "in": "path",
18512
+ "required": true,
18513
+ "schema": {
18514
+ "type": "string",
18515
+ "format": "uuid"
18516
+ }
18517
+ }
18518
+ ],
18519
+ "responses": {
18520
+ "200": {
18521
+ "description": "Trust signals",
18522
+ "content": {
18523
+ "application/json": {
18524
+ "schema": {
18525
+ "type": "object",
18526
+ "properties": {
18527
+ "trust": {
18528
+ "type": "object",
18529
+ "properties": {
18530
+ "tier": {
18531
+ "type": "string",
18532
+ "enum": [
18533
+ "unverified",
18534
+ "platform_reviewed",
18535
+ "identity_verified",
18536
+ "enterprise"
18537
+ ]
18538
+ },
18539
+ "install_count": {
18540
+ "type": "integer"
18541
+ },
18542
+ "avg_rating": {
18543
+ "type": "number",
18544
+ "nullable": true
18545
+ },
18546
+ "review_count": {
18547
+ "type": "integer"
18548
+ },
18549
+ "badges": {
18550
+ "type": "array",
18551
+ "items": {
18552
+ "type": "string"
18553
+ }
18554
+ },
18555
+ "flagged_for_review": {
18556
+ "type": "boolean"
18557
+ }
18558
+ }
18559
+ }
18560
+ }
18561
+ }
18562
+ }
18563
+ }
18564
+ },
18565
+ "404": {
18566
+ "$ref": "#/components/responses/NotFound"
18567
+ }
18568
+ }
18569
+ }
18570
+ },
18571
+ "/v1/agents/{agent_id}/report": {
18572
+ "post": {
18573
+ "tags": [
18574
+ "Discovery"
18575
+ ],
18576
+ "summary": "Report a listed agent",
18577
+ "description": "Human users only — an agent reporting another agent is a way to bury a\ncompetitor's listing at machine speed.\n\nThe response does not include the report count. Telling a reporter how\nclose a listing is to being flagged tells them how many more to file.\n",
18578
+ "operationId": "reportAgent",
18579
+ "parameters": [
18580
+ {
18581
+ "name": "agent_id",
18582
+ "in": "path",
18583
+ "required": true,
18584
+ "schema": {
18585
+ "type": "string",
18586
+ "format": "uuid"
18587
+ }
18588
+ }
18589
+ ],
18590
+ "requestBody": {
18591
+ "required": true,
18592
+ "content": {
18593
+ "application/json": {
18594
+ "schema": {
18595
+ "type": "object",
18596
+ "properties": {
18597
+ "reason": {
18598
+ "type": "string"
18599
+ }
18600
+ }
18601
+ }
18602
+ }
18603
+ }
18604
+ },
18605
+ "responses": {
18606
+ "202": {
18607
+ "description": "Received"
18608
+ },
18609
+ "403": {
18610
+ "$ref": "#/components/responses/Forbidden"
18611
+ },
18612
+ "404": {
18613
+ "$ref": "#/components/responses/NotFound"
18614
+ }
18615
+ }
18616
+ }
18617
+ },
18618
+ "/v1/agents/{agent_id}/review": {
18619
+ "post": {
18620
+ "tags": [
18621
+ "Discovery"
18622
+ ],
18623
+ "summary": "Rate an agent you have used",
18624
+ "description": "One review per person per agent; a second replaces the first. You cannot\nreview an agent from your own organisation.\n\nComments are shown only once moderated. The rating counts either way — a\nnumber is harder to abuse than free text.\n",
18625
+ "operationId": "reviewAgent",
18626
+ "parameters": [
18627
+ {
18628
+ "name": "agent_id",
18629
+ "in": "path",
18630
+ "required": true,
18631
+ "schema": {
18632
+ "type": "string",
18633
+ "format": "uuid"
18634
+ }
18635
+ }
18636
+ ],
18637
+ "requestBody": {
18638
+ "required": true,
18639
+ "content": {
18640
+ "application/json": {
18641
+ "schema": {
18642
+ "type": "object",
18643
+ "required": [
18644
+ "rating"
18645
+ ],
18646
+ "properties": {
18647
+ "rating": {
18648
+ "type": "integer",
18649
+ "minimum": 1,
18650
+ "maximum": 5
18651
+ },
18652
+ "comment": {
18653
+ "type": "string"
18654
+ }
18655
+ }
18656
+ }
18657
+ }
18658
+ }
18659
+ },
18660
+ "responses": {
18661
+ "201": {
18662
+ "description": "Recorded"
18663
+ },
18664
+ "400": {
18665
+ "$ref": "#/components/responses/BadRequest"
18666
+ },
18667
+ "403": {
18668
+ "$ref": "#/components/responses/Forbidden"
18669
+ },
18670
+ "404": {
18671
+ "$ref": "#/components/responses/NotFound"
18672
+ }
18673
+ }
18674
+ }
18675
+ },
18676
+ "/v1/peers": {
18677
+ "post": {
18678
+ "tags": [
18679
+ "Memory"
18680
+ ],
18681
+ "summary": "Create a peer and name its observers",
18682
+ "description": "Human users only. Creating a peer decides which agents may read a model\nof a person, and an agent that could do that could add itself.\n\nIdempotent on `(org, peer_type, peer_ref)`. Observers are **merged**, not\nreplaced — a second call adding one agent does not revoke the others\nalready watching. Every named observer must be an agent in this\norganisation, so a typo or an id copied from elsewhere is an error rather\nthan a silent no-op that leaves an operator believing an agent is\nwatching when none is.\n",
18683
+ "operationId": "createPeer",
18684
+ "requestBody": {
18685
+ "required": true,
18686
+ "content": {
18687
+ "application/json": {
18688
+ "schema": {
18689
+ "type": "object",
18690
+ "required": [
18691
+ "peer_type",
18692
+ "peer_ref"
18693
+ ],
18694
+ "properties": {
18695
+ "peer_type": {
18696
+ "type": "string",
18697
+ "enum": [
18698
+ "user",
18699
+ "platform_connection",
18700
+ "external"
18701
+ ]
18702
+ },
18703
+ "peer_ref": {
18704
+ "type": "string"
18705
+ },
18706
+ "display_name": {
18707
+ "type": "string"
18708
+ },
18709
+ "platform_connection_id": {
18710
+ "type": "string",
18711
+ "format": "uuid"
18712
+ },
18713
+ "observer_agent_ids": {
18714
+ "type": "array",
18715
+ "items": {
18716
+ "type": "string",
18717
+ "format": "uuid"
18718
+ },
18719
+ "description": "Empty means nobody. A peer with no observers is readable by no agent."
18720
+ }
18721
+ }
18722
+ }
18723
+ }
18724
+ }
18725
+ },
18726
+ "responses": {
18727
+ "201": {
18728
+ "description": "Peer created or updated",
18729
+ "content": {
18730
+ "application/json": {
18731
+ "schema": {
18732
+ "type": "object",
18733
+ "properties": {
18734
+ "peer": {
18735
+ "$ref": "#/components/schemas/Peer"
18736
+ }
18737
+ }
18738
+ }
18739
+ }
18740
+ }
18741
+ },
18742
+ "400": {
18743
+ "$ref": "#/components/responses/BadRequest"
18744
+ },
18745
+ "403": {
18746
+ "$ref": "#/components/responses/Forbidden"
18747
+ }
18748
+ }
18749
+ }
18750
+ },
18751
+ "/v1/peers/{peer_id}/export": {
18752
+ "get": {
18753
+ "tags": [
18754
+ "Memory"
18755
+ ],
18756
+ "summary": "Everything held about this person",
18757
+ "description": "The whole behavioural profile plus the raw observations behind it. Human\nusers only, behind strong-factor re-auth — this is exactly what a stolen\nsession would want.\n\nEach fact carries `why_we_believe_this`: its provenance, including\ntombstones for observations that have since expired. An export listing\nconclusions without their basis answers only the easy half of the\nquestion.\n",
18758
+ "operationId": "exportPeerData",
18759
+ "parameters": [
18760
+ {
18761
+ "name": "peer_id",
18762
+ "in": "path",
18763
+ "required": true,
18764
+ "schema": {
18765
+ "type": "string",
18766
+ "format": "uuid"
18767
+ }
18768
+ }
18769
+ ],
18770
+ "responses": {
18771
+ "200": {
18772
+ "description": "Export",
18773
+ "content": {
18774
+ "application/json": {
18775
+ "schema": {
18776
+ "type": "object",
18777
+ "properties": {
18778
+ "peer": {
18779
+ "type": "object"
18780
+ },
18781
+ "facts": {
18782
+ "type": "array",
18783
+ "items": {
18784
+ "type": "object",
18785
+ "properties": {
18786
+ "fact_key": {
18787
+ "type": "string"
18788
+ },
18789
+ "fact_value": {
18790
+ "type": "object"
18791
+ },
18792
+ "confidence": {
18793
+ "type": "string",
18794
+ "nullable": true
18795
+ },
18796
+ "why_we_believe_this": {
18797
+ "type": "array",
18798
+ "items": {
18799
+ "type": "object"
18800
+ }
18801
+ },
18802
+ "corrected_by_a_human": {
18803
+ "type": "boolean"
18804
+ }
18805
+ }
18806
+ }
18807
+ },
18808
+ "raw_observations": {
18809
+ "type": "array",
18810
+ "items": {
18811
+ "type": "object"
18812
+ }
18813
+ },
18814
+ "note": {
18815
+ "type": "string"
18816
+ }
18817
+ }
18818
+ }
18819
+ }
18820
+ }
18821
+ },
18822
+ "403": {
18823
+ "$ref": "#/components/responses/Forbidden"
18824
+ },
18825
+ "404": {
18826
+ "$ref": "#/components/responses/NotFound"
18827
+ }
18828
+ }
18829
+ }
18830
+ },
18831
+ "/v1/peers/{peer_id}/data": {
18832
+ "delete": {
18833
+ "tags": [
18834
+ "Memory"
18835
+ ],
18836
+ "summary": "Forget this person",
18837
+ "description": "Deletes the peer, its facts and its observations. Human users only,\nbehind strong-factor re-auth, and irreversible.\n\nReturns counts of what was removed — \"deleted\" with no numbers is not\nsomething anyone can check. The audit entry records that a deletion\nhappened and deliberately omits the identifier someone asked to have\nforgotten.\n",
18838
+ "operationId": "deletePeerData",
18839
+ "parameters": [
18840
+ {
18841
+ "name": "peer_id",
18842
+ "in": "path",
18843
+ "required": true,
18844
+ "schema": {
18845
+ "type": "string",
18846
+ "format": "uuid"
18847
+ }
18848
+ }
18849
+ ],
18850
+ "responses": {
18851
+ "200": {
18852
+ "description": "Deleted",
18853
+ "content": {
18854
+ "application/json": {
18855
+ "schema": {
18856
+ "type": "object",
18857
+ "properties": {
18858
+ "deleted": {
18859
+ "type": "boolean"
18860
+ },
18861
+ "facts_deleted": {
18862
+ "type": "integer"
18863
+ },
18864
+ "observations_deleted": {
18865
+ "type": "integer"
18866
+ }
18867
+ }
18868
+ }
18869
+ }
18870
+ }
18871
+ },
18872
+ "403": {
18873
+ "$ref": "#/components/responses/Forbidden"
18874
+ },
18875
+ "404": {
18876
+ "$ref": "#/components/responses/NotFound"
18877
+ }
18878
+ }
18879
+ }
18880
+ },
18881
+ "/v1/peers/{peer_id}/facts": {
18882
+ "patch": {
18883
+ "tags": [
18884
+ "Memory"
18885
+ ],
18886
+ "summary": "Correct what the system believes",
18887
+ "description": "Human users only. A correction **pins** the fact: the background\nprocessor will not re-derive over it, because someone correcting what a\nsystem believes about them outranks the inference that got it wrong.\n\nThe correction is appended to the fact's provenance as a `human` entry,\nso the record shows both what was inferred and that a person disagreed.\n",
18888
+ "operationId": "editPeerFact",
18889
+ "parameters": [
18890
+ {
18891
+ "name": "peer_id",
18892
+ "in": "path",
18893
+ "required": true,
18894
+ "schema": {
18895
+ "type": "string",
18896
+ "format": "uuid"
18897
+ }
18898
+ }
18899
+ ],
18900
+ "requestBody": {
18901
+ "required": true,
18902
+ "content": {
18903
+ "application/json": {
18904
+ "schema": {
18905
+ "type": "object",
18906
+ "required": [
18907
+ "fact_key",
18908
+ "fact_value"
18909
+ ],
18910
+ "properties": {
18911
+ "fact_key": {
18912
+ "type": "string"
18913
+ },
18914
+ "fact_value": {
18915
+ "type": "object"
18916
+ }
18917
+ }
18918
+ }
18919
+ }
18920
+ }
18921
+ },
18922
+ "responses": {
18923
+ "200": {
18924
+ "description": "Corrected",
18925
+ "content": {
18926
+ "application/json": {
18927
+ "schema": {
18928
+ "type": "object",
18929
+ "properties": {
18930
+ "fact_key": {
18931
+ "type": "string"
18932
+ },
18933
+ "edited_by_human": {
18934
+ "type": "boolean"
18935
+ },
18936
+ "note": {
18937
+ "type": "string"
18938
+ }
18939
+ }
18940
+ }
18941
+ }
18942
+ }
18943
+ },
18944
+ "403": {
18945
+ "$ref": "#/components/responses/Forbidden"
18946
+ },
18947
+ "404": {
18948
+ "$ref": "#/components/responses/NotFound"
18949
+ }
18950
+ }
18951
+ }
18952
+ },
18953
+ "/v1/peers/{peer_id}/context": {
18954
+ "get": {
18955
+ "tags": [
18956
+ "Memory"
18957
+ ],
18958
+ "summary": "A context blob for prompt injection",
18959
+ "description": "What this person's history suggests, as prose an agent can put in a\nprompt. Best-supported facts first, so a tight budget drops the\nleast-supported beliefs rather than an arbitrary tail — and a fact a\nhuman corrected sorts ahead of everything, because a correction someone\ntook the trouble to make is the last thing to cut.\n\nNever truncates mid-line: half a sentence about a person is worse than\none fewer sentence. A peer with no facts returns an empty string rather\nthan a header claiming to describe someone.\n\nThe blob ends by saying these are observations and not instructions,\nbecause an agent reading it needs to know the difference.\n",
18960
+ "operationId": "getPeerContext",
18961
+ "parameters": [
18962
+ {
18963
+ "name": "peer_id",
18964
+ "in": "path",
18965
+ "required": true,
18966
+ "schema": {
18967
+ "type": "string",
18968
+ "format": "uuid"
18969
+ }
18970
+ },
18971
+ {
18972
+ "name": "budget",
18973
+ "in": "query",
18974
+ "description": "Characters. Default 2000, capped at 8000.",
18975
+ "schema": {
18976
+ "type": "integer"
18977
+ }
18978
+ }
18979
+ ],
18980
+ "responses": {
18981
+ "200": {
18982
+ "description": "Context",
18983
+ "content": {
18984
+ "application/json": {
18985
+ "schema": {
18986
+ "type": "object",
18987
+ "properties": {
18988
+ "context": {
18989
+ "type": "string"
18990
+ },
18991
+ "characters": {
18992
+ "type": "integer"
18993
+ },
18994
+ "budget": {
18995
+ "type": "integer"
18996
+ },
18997
+ "facts_available": {
18998
+ "type": "integer"
18999
+ }
19000
+ }
19001
+ }
19002
+ }
19003
+ }
19004
+ },
19005
+ "403": {
19006
+ "$ref": "#/components/responses/Forbidden"
19007
+ },
19008
+ "404": {
19009
+ "$ref": "#/components/responses/NotFound"
19010
+ }
19011
+ }
19012
+ }
19013
+ },
19014
+ "/v1/agents/{agent_id}/peer-context": {
19015
+ "get": {
19016
+ "tags": [
19017
+ "Memory"
19018
+ ],
19019
+ "summary": "An agent's own peer context",
19020
+ "description": "Resolves the peer from the agent's platform connection, so an agent does\nnot need to know a peer id.\n\nAn agent may only ask for its own — otherwise this route would be a way\nto read a peer through an agent that observes it, from one that does not.\nThe observer check still applies: being the agent named in the path is\nnot the same as observing that connection's peer.\n",
19021
+ "operationId": "getAgentPeerContext",
19022
+ "parameters": [
19023
+ {
19024
+ "name": "agent_id",
19025
+ "in": "path",
19026
+ "required": true,
19027
+ "schema": {
19028
+ "type": "string",
19029
+ "format": "uuid"
19030
+ }
19031
+ },
19032
+ {
19033
+ "name": "budget",
19034
+ "in": "query",
19035
+ "schema": {
19036
+ "type": "integer"
19037
+ }
19038
+ }
19039
+ ],
19040
+ "responses": {
19041
+ "200": {
19042
+ "description": "Context",
19043
+ "content": {
19044
+ "application/json": {
19045
+ "schema": {
19046
+ "type": "object",
19047
+ "properties": {
19048
+ "peer_id": {
19049
+ "type": "string",
19050
+ "format": "uuid"
19051
+ },
19052
+ "context": {
19053
+ "type": "string"
19054
+ },
19055
+ "characters": {
19056
+ "type": "integer"
19057
+ }
19058
+ }
19059
+ }
19060
+ }
19061
+ }
19062
+ },
19063
+ "403": {
19064
+ "$ref": "#/components/responses/Forbidden"
19065
+ },
19066
+ "404": {
19067
+ "$ref": "#/components/responses/NotFound"
19068
+ }
19069
+ }
19070
+ }
19071
+ },
19072
+ "/v1/peers/{peer_id}": {
19073
+ "get": {
19074
+ "tags": [
19075
+ "Memory"
19076
+ ],
19077
+ "summary": "A peer's profile and derived facts",
19078
+ "description": "A peer is a shared model of one human, across the agents serving them.\n\n**An agent reaches a peer only by being named in its observer list.**\nBeing in the same organisation, the same platform connection, or holding\na broad scope grants nothing. A peer with no observers is readable by no\nagent at all — forgetting to set observers must not expose someone's\nbehavioural profile to every agent in the org.\n\nA peer in another organisation returns 404, the same as an unknown id:\nwhether one exists elsewhere is not something a caller should learn.\n\nEach fact carries `provenance` — why it is believed. Entries for events\nthat have since expired become tombstones keeping the shape of what was\nseen without the content, so a belief about a person always has a\nrecoverable basis.\n",
19079
+ "operationId": "getPeer",
19080
+ "parameters": [
19081
+ {
19082
+ "name": "peer_id",
19083
+ "in": "path",
19084
+ "required": true,
19085
+ "schema": {
19086
+ "type": "string",
19087
+ "format": "uuid"
19088
+ }
19089
+ }
19090
+ ],
19091
+ "responses": {
19092
+ "200": {
19093
+ "description": "Peer and facts",
19094
+ "content": {
19095
+ "application/json": {
19096
+ "schema": {
19097
+ "type": "object",
19098
+ "properties": {
19099
+ "peer": {
19100
+ "$ref": "#/components/schemas/Peer"
19101
+ },
19102
+ "facts": {
19103
+ "type": "array",
19104
+ "items": {
19105
+ "$ref": "#/components/schemas/PeerFact"
19106
+ }
19107
+ }
19108
+ }
19109
+ }
19110
+ }
19111
+ }
19112
+ },
19113
+ "403": {
19114
+ "$ref": "#/components/responses/Forbidden"
19115
+ },
19116
+ "404": {
19117
+ "$ref": "#/components/responses/NotFound"
19118
+ }
19119
+ }
19120
+ }
19121
+ },
19122
+ "/v1/peers/{peer_id}/events": {
19123
+ "post": {
19124
+ "tags": [
19125
+ "Memory"
19126
+ ],
19127
+ "summary": "Record something observed about a person",
19128
+ "operationId": "recordPeerEvent",
19129
+ "parameters": [
19130
+ {
19131
+ "name": "peer_id",
19132
+ "in": "path",
19133
+ "required": true,
19134
+ "schema": {
19135
+ "type": "string",
19136
+ "format": "uuid"
19137
+ }
19138
+ }
19139
+ ],
19140
+ "requestBody": {
19141
+ "required": true,
19142
+ "content": {
19143
+ "application/json": {
19144
+ "schema": {
19145
+ "type": "object",
19146
+ "required": [
19147
+ "event_type",
19148
+ "content"
19149
+ ],
19150
+ "properties": {
19151
+ "event_type": {
19152
+ "type": "string",
19153
+ "enum": [
19154
+ "message",
19155
+ "approval",
19156
+ "action",
19157
+ "observation"
19158
+ ]
19159
+ },
19160
+ "content": {
19161
+ "type": "object",
19162
+ "additionalProperties": true
19163
+ }
19164
+ }
19165
+ }
19166
+ }
19167
+ }
19168
+ },
19169
+ "responses": {
19170
+ "201": {
19171
+ "description": "Recorded",
19172
+ "content": {
19173
+ "application/json": {
19174
+ "schema": {
19175
+ "type": "object",
19176
+ "properties": {
19177
+ "event_id": {
19178
+ "type": "string",
19179
+ "format": "uuid"
19180
+ }
19181
+ }
19182
+ }
19183
+ }
19184
+ }
19185
+ },
19186
+ "400": {
19187
+ "$ref": "#/components/responses/BadRequest"
19188
+ },
19189
+ "403": {
19190
+ "$ref": "#/components/responses/Forbidden"
19191
+ },
19192
+ "404": {
19193
+ "$ref": "#/components/responses/NotFound"
19194
+ }
19195
+ }
19196
+ }
19197
+ },
19198
+ "/v1/peers/{peer_id}/predict-approval": {
19199
+ "post": {
19200
+ "tags": [
19201
+ "Memory"
19202
+ ],
19203
+ "summary": "How has this person decided this before?",
19204
+ "description": "Answers two different questions, and keeps them apart on purpose.\n\n`likelihood` is an **observation about a person** — how they have decided\ncomparable requests before. Comparable means the same fingerprint bucket,\nnot the same action type: three approvals of $5 say nothing about $500.\n\n`suggest_auto` is a **statement about a policy**. It is true only where a\nrule the operator already wrote would auto-approve this exact case. It is\nnever derived from `likelihood`, and a confident model never becomes new\nauthority. It is false, with `blocked_reason`, when:\n\n* no rule covers the action (`no_matching_rule`);\n* a rule says a human decides (`rule_requires_approval`);\n* the amount is above the rule's own threshold (`above_configured_threshold`);\n* the derived risk tier is above the lowest (`risk_tier_requires_step_up`);\n* the action grants or destroys authority (`action_is_sensitive`).\n\nThe policy consulted is the calling agent's own, so a prediction cannot\ninherit authority from another agent that happens to observe the same\nperson.\n",
19205
+ "operationId": "predictApproval",
19206
+ "parameters": [
19207
+ {
19208
+ "name": "peer_id",
19209
+ "in": "path",
19210
+ "required": true,
19211
+ "schema": {
19212
+ "type": "string",
19213
+ "format": "uuid"
19214
+ }
19215
+ }
19216
+ ],
19217
+ "requestBody": {
19218
+ "required": true,
19219
+ "content": {
19220
+ "application/json": {
19221
+ "schema": {
19222
+ "type": "object",
19223
+ "required": [
19224
+ "action_type",
19225
+ "effective_risk_tier"
19226
+ ],
19227
+ "properties": {
19228
+ "action_type": {
19229
+ "type": "string",
19230
+ "example": "refund.create"
19231
+ },
19232
+ "payload": {
19233
+ "type": "object",
19234
+ "additionalProperties": true
19235
+ },
19236
+ "effective_risk_tier": {
19237
+ "type": "integer",
19238
+ "minimum": 1,
19239
+ "maximum": 3
19240
+ }
19241
+ }
19242
+ }
19243
+ }
19244
+ }
19245
+ },
19246
+ "responses": {
19247
+ "200": {
19248
+ "description": "Prediction",
19249
+ "content": {
19250
+ "application/json": {
19251
+ "schema": {
19252
+ "type": "object",
19253
+ "required": [
19254
+ "reasoning",
19255
+ "suggest_auto"
19256
+ ],
19257
+ "properties": {
19258
+ "likelihood": {
19259
+ "type": "number",
19260
+ "nullable": true,
19261
+ "description": "Absent when there is no comparable history."
19262
+ },
19263
+ "reasoning": {
19264
+ "type": "string"
19265
+ },
19266
+ "suggest_auto": {
19267
+ "type": "boolean",
19268
+ "description": "Whether the operator's own policy already permits this."
19269
+ },
19270
+ "blocked_reason": {
19271
+ "type": "string",
19272
+ "enum": [
19273
+ "no_matching_rule",
19274
+ "rule_requires_approval",
19275
+ "above_configured_threshold",
19276
+ "risk_tier_requires_step_up",
19277
+ "action_is_sensitive"
19278
+ ]
19279
+ }
19280
+ }
19281
+ }
19282
+ }
19283
+ }
19284
+ },
19285
+ "403": {
19286
+ "$ref": "#/components/responses/Forbidden"
19287
+ },
19288
+ "404": {
19289
+ "$ref": "#/components/responses/NotFound"
19290
+ }
19291
+ }
19292
+ }
19293
+ },
19294
+ "/v1/peers/by-connection/{connection_id}": {
19295
+ "get": {
19296
+ "tags": [
19297
+ "Memory"
19298
+ ],
19299
+ "summary": "Resolve the peer for a platform connection",
19300
+ "operationId": "getPeerByConnection",
19301
+ "parameters": [
19302
+ {
19303
+ "name": "connection_id",
19304
+ "in": "path",
19305
+ "required": true,
19306
+ "schema": {
19307
+ "type": "string",
19308
+ "format": "uuid"
19309
+ }
19310
+ }
19311
+ ],
19312
+ "responses": {
19313
+ "200": {
19314
+ "description": "Peer",
19315
+ "content": {
19316
+ "application/json": {
19317
+ "schema": {
19318
+ "type": "object",
19319
+ "properties": {
19320
+ "peer": {
19321
+ "$ref": "#/components/schemas/Peer"
19322
+ }
19323
+ }
19324
+ }
19325
+ }
19326
+ }
19327
+ },
19328
+ "403": {
19329
+ "$ref": "#/components/responses/Forbidden"
19330
+ },
19331
+ "404": {
19332
+ "$ref": "#/components/responses/NotFound"
19333
+ }
19334
+ }
19335
+ }
19336
+ },
19337
+ "/v1/org/apply/diff": {
19338
+ "post": {
19339
+ "tags": [
19340
+ "Organization"
19341
+ ],
19342
+ "summary": "What would this chart change?",
19343
+ "description": "Reconcile a chart against the org without changing anything. Human users\nonly — a chart provisions agents, vaults and access policies, so an agent\nthat could apply one could grant itself access to a vault it cannot\ncurrently read.\n\nA POST rather than a GET because the chart is the request body, and a GET\ncarrying a body is one many proxies and clients drop or mangle. Read-only\neither way.\n\nThe plan reports five outcomes per resource:\n\n* `create` — not present, would be created.\n* `patch` — present, differs only in fields safe to change in place.\n* `unchanged` — present and matching.\n* `skipped_drifted` — **changed outside this chart**, so left alone.\n Someone edited it by hand for a reason; overwriting that because a file\n says otherwise is how a deployment tool destroys an incident fix.\n* `refused` — the chart asks for a change apply will not make. Guardrail\n fields are never patched here: editing them routes through the guardrail\n approval flow, and a reconciler writing them directly would be a way\n around it.\n\nPass `applied_state` from `.1claw/apply-state.json` so drift can be told\nfrom a first run — a pre-existing resource is not drifted, it was simply\nnot created by this chart.\n",
19344
+ "operationId": "diffChart",
19345
+ "requestBody": {
19346
+ "required": true,
19347
+ "content": {
19348
+ "application/json": {
19349
+ "schema": {
19350
+ "type": "object",
19351
+ "required": [
19352
+ "chart"
19353
+ ],
19354
+ "properties": {
19355
+ "chart": {
19356
+ "type": "object",
19357
+ "description": "A chart document — `apiVersion`, `kind`, `metadata`, `spec`."
19358
+ },
19359
+ "applied_state": {
19360
+ "type": "object",
19361
+ "additionalProperties": true,
19362
+ "description": "What a previous apply recorded, keyed `kind/name`."
19363
+ }
19364
+ }
19365
+ }
19366
+ }
19367
+ }
19368
+ },
19369
+ "responses": {
19370
+ "200": {
19371
+ "description": "The plan",
19372
+ "content": {
19373
+ "application/json": {
19374
+ "schema": {
19375
+ "type": "object",
19376
+ "properties": {
19377
+ "chart_name": {
19378
+ "type": "string"
19379
+ },
19380
+ "actions": {
19381
+ "type": "array",
19382
+ "items": {
19383
+ "type": "object"
19384
+ }
19385
+ },
19386
+ "warnings": {
19387
+ "type": "array",
19388
+ "items": {
19389
+ "type": "string"
19390
+ }
19391
+ },
19392
+ "summary": {
19393
+ "type": "object",
19394
+ "properties": {
19395
+ "create": {
19396
+ "type": "integer"
19397
+ },
19398
+ "patch": {
19399
+ "type": "integer"
19400
+ },
19401
+ "skipped_drifted": {
19402
+ "type": "integer"
19403
+ },
19404
+ "unchanged": {
19405
+ "type": "integer"
19406
+ },
19407
+ "refused": {
19408
+ "type": "integer"
19409
+ },
19410
+ "no_changes": {
19411
+ "type": "boolean"
19412
+ }
19413
+ }
19414
+ }
19415
+ }
19416
+ }
19417
+ }
19418
+ }
19419
+ },
19420
+ "400": {
19421
+ "$ref": "#/components/responses/BadRequest"
19422
+ },
19423
+ "403": {
19424
+ "$ref": "#/components/responses/Forbidden"
19425
+ }
19426
+ }
19427
+ }
19428
+ },
19429
+ "/v1/notification-targets": {
19430
+ "get": {
19431
+ "tags": [
19432
+ "Notifications"
19433
+ ],
19434
+ "summary": "List your notification targets",
19435
+ "operationId": "listNotificationTargets",
19436
+ "responses": {
19437
+ "200": {
19438
+ "description": "Your targets",
19439
+ "content": {
19440
+ "application/json": {
19441
+ "schema": {
19442
+ "type": "object",
19443
+ "properties": {
19444
+ "targets": {
19445
+ "type": "array",
19446
+ "items": {
19447
+ "$ref": "#/components/schemas/NotificationTarget"
19448
+ }
19449
+ }
19450
+ }
19451
+ }
19452
+ }
19453
+ }
19454
+ }
19455
+ }
19456
+ },
19457
+ "post": {
19458
+ "tags": [
19459
+ "Notifications"
19460
+ ],
19461
+ "summary": "Add a notification target",
19462
+ "description": "Where approvals and automation output reach a human: a phone number, an\nhttps webhook, an email address, or a push token.\n\nAn SMS target is created **unverified** and stays that way until someone\nproves they hold the number. Adding a number must not itself be an\nauthorisation — otherwise a session borrowed for five minutes leaves\nbehind a number that can approve things long after it is gone. An\nunverified target still receives notifications; it just cannot reply to\ndecide one.\n",
19463
+ "operationId": "createNotificationTarget",
19464
+ "requestBody": {
19465
+ "required": true,
19466
+ "content": {
19467
+ "application/json": {
19468
+ "schema": {
19469
+ "type": "object",
19470
+ "required": [
19471
+ "target_type",
19472
+ "config"
19473
+ ],
19474
+ "properties": {
19475
+ "target_type": {
19476
+ "type": "string",
19477
+ "enum": [
19478
+ "sms",
19479
+ "webhook",
19480
+ "expo",
19481
+ "email"
19482
+ ]
19483
+ },
19484
+ "config": {
19485
+ "type": "object",
19486
+ "description": "`{\"phone_number\": \"+14155550123\"}` for sms (E.164 only),\n`{\"url\": \"https://…\"}` for webhook (https only),\n`{\"email\": \"…\"}`, or `{\"push_token\": \"…\"}`.\n"
19487
+ },
19488
+ "events": {
19489
+ "type": "array",
19490
+ "items": {
19491
+ "type": "string"
19492
+ },
19493
+ "description": "Empty means every event."
19494
+ },
19495
+ "agent_id": {
19496
+ "type": "string",
19497
+ "format": "uuid",
19498
+ "description": "The agent whose SMS channel sends to this target."
19499
+ },
19500
+ "user_id": {
19501
+ "type": "string",
19502
+ "format": "uuid",
19503
+ "description": "Defaults to the caller."
19504
+ }
19505
+ }
19506
+ }
19507
+ }
19508
+ }
19509
+ },
19510
+ "responses": {
19511
+ "201": {
19512
+ "description": "Target created, unverified",
19513
+ "content": {
19514
+ "application/json": {
19515
+ "schema": {
19516
+ "$ref": "#/components/schemas/NotificationTarget"
19517
+ }
19518
+ }
19519
+ }
19520
+ },
19521
+ "400": {
19522
+ "$ref": "#/components/responses/BadRequest"
19523
+ },
19524
+ "403": {
19525
+ "$ref": "#/components/responses/Forbidden"
19526
+ },
19527
+ "409": {
19528
+ "$ref": "#/components/responses/Conflict"
19529
+ }
19530
+ }
19531
+ }
19532
+ },
19533
+ "/v1/notification-targets/{id}": {
19534
+ "delete": {
19535
+ "tags": [
19536
+ "Notifications"
19537
+ ],
19538
+ "summary": "Remove a notification target",
19539
+ "operationId": "deleteNotificationTarget",
19540
+ "parameters": [
19541
+ {
19542
+ "name": "id",
19543
+ "in": "path",
19544
+ "required": true,
19545
+ "schema": {
19546
+ "type": "string",
19547
+ "format": "uuid"
19548
+ }
19549
+ }
19550
+ ],
19551
+ "responses": {
19552
+ "204": {
19553
+ "description": "Removed"
19554
+ },
19555
+ "403": {
19556
+ "$ref": "#/components/responses/Forbidden"
19557
+ },
19558
+ "404": {
19559
+ "$ref": "#/components/responses/NotFound"
19560
+ }
19561
+ }
19562
+ }
19563
+ },
19564
+ "/v1/notification-targets/{id}/verify/start": {
19565
+ "post": {
19566
+ "tags": [
19567
+ "Notifications"
19568
+ ],
19569
+ "summary": "Text a verification code to an SMS target",
19570
+ "description": "Sends a six-digit code from the same channel the target's notifications\nwill come from — a code arriving from a different number than the one\nthe recipient will later see is a code they are right to distrust.\n\nExpires in 10 minutes. Five wrong answers void it.\n",
19571
+ "operationId": "startNotificationTargetVerification",
19572
+ "parameters": [
19573
+ {
19574
+ "name": "id",
19575
+ "in": "path",
19576
+ "required": true,
19577
+ "schema": {
19578
+ "type": "string",
19579
+ "format": "uuid"
19580
+ }
19581
+ }
19582
+ ],
19583
+ "responses": {
19584
+ "200": {
19585
+ "description": "Code sent",
19586
+ "content": {
19587
+ "application/json": {
19588
+ "schema": {
19589
+ "type": "object",
19590
+ "properties": {
19591
+ "message": {
19592
+ "type": "string"
19593
+ },
19594
+ "expires_in_seconds": {
19595
+ "type": "integer"
19596
+ }
19597
+ }
19598
+ }
19599
+ }
19600
+ }
19601
+ },
19602
+ "400": {
19603
+ "$ref": "#/components/responses/BadRequest"
19604
+ },
19605
+ "403": {
19606
+ "$ref": "#/components/responses/Forbidden"
19607
+ },
19608
+ "404": {
19609
+ "$ref": "#/components/responses/NotFound"
19610
+ }
19611
+ }
19612
+ }
19613
+ },
19614
+ "/v1/notification-targets/{id}/verify": {
19615
+ "post": {
19616
+ "tags": [
19617
+ "Notifications"
19618
+ ],
19619
+ "summary": "Submit the verification code",
19620
+ "operationId": "completeNotificationTargetVerification",
19621
+ "parameters": [
19622
+ {
19623
+ "name": "id",
19624
+ "in": "path",
19625
+ "required": true,
19626
+ "schema": {
19627
+ "type": "string",
19628
+ "format": "uuid"
19629
+ }
19630
+ }
19631
+ ],
19632
+ "requestBody": {
19633
+ "required": true,
19634
+ "content": {
19635
+ "application/json": {
19636
+ "schema": {
19637
+ "type": "object",
19638
+ "required": [
19639
+ "code"
19640
+ ],
19641
+ "properties": {
19642
+ "code": {
19643
+ "type": "string",
19644
+ "example": "042913"
19645
+ }
19646
+ }
19647
+ }
19648
+ }
19649
+ }
19650
+ },
19651
+ "responses": {
19652
+ "200": {
19653
+ "description": "Verified",
19654
+ "content": {
19655
+ "application/json": {
19656
+ "schema": {
19657
+ "type": "object",
19658
+ "properties": {
19659
+ "verified": {
19660
+ "type": "boolean"
19661
+ }
19662
+ }
19663
+ }
19664
+ }
19665
+ }
19666
+ },
19667
+ "400": {
19668
+ "$ref": "#/components/responses/BadRequest"
19669
+ },
19670
+ "403": {
19671
+ "$ref": "#/components/responses/Forbidden"
19672
+ },
19673
+ "404": {
19674
+ "$ref": "#/components/responses/NotFound"
19675
+ }
19676
+ }
19677
+ }
19678
+ },
19679
+ "/v1/webhooks/sms/{webhook_path}": {
19680
+ "post": {
19681
+ "tags": [
19682
+ "Channels"
19683
+ ],
19684
+ "summary": "Inbound SMS webhook (Twilio)",
19685
+ "description": "Called by Twilio when someone texts the channel's number. Public, because\nthe provider calls it — so the `X-Twilio-Signature` header is the only\nthing establishing that a message is genuine, and it is verified over the\nexact public URL and every POST parameter.\n\nA verified signature proves the message came from Twilio, **not** that it\ncame from the right person: anyone who knows the number can text it and\ntheir message arrives correctly signed. So the sending number must also\nmatch a *verified* SMS notification target.\n\nA reply may decide an approval only when its server-derived\n`risk_tier` is 1. Anything higher is answered with a link to confirm in\nthe app; replying cannot decide it. When more than one approval is\npending, a bare YES/NO is answered with a request for the reference code\nrather than applied to a guess.\n\nAlways answers 200 with TwiML — a non-2xx makes Twilio retry a message\nthat was deliberately refused. The exception is a bad signature, which is\n403, because a persistently failing signature is a misconfiguration as\noften as an attack and silence would hide both.\n",
19686
+ "operationId": "smsWebhook",
19687
+ "security": [],
19688
+ "parameters": [
19689
+ {
19690
+ "name": "webhook_path",
19691
+ "in": "path",
19692
+ "required": true,
19693
+ "schema": {
19694
+ "type": "string"
19695
+ }
19696
+ },
19697
+ {
19698
+ "name": "X-Twilio-Signature",
19699
+ "in": "header",
19700
+ "required": true,
19701
+ "schema": {
19702
+ "type": "string"
19703
+ }
19704
+ }
19705
+ ],
19706
+ "requestBody": {
19707
+ "required": true,
19708
+ "content": {
19709
+ "application/x-www-form-urlencoded": {
19710
+ "schema": {
19711
+ "type": "object",
19712
+ "properties": {
19713
+ "From": {
19714
+ "type": "string",
19715
+ "example": "+14155550123"
19716
+ },
19717
+ "To": {
19718
+ "type": "string",
19719
+ "example": "+14155550999"
19720
+ },
19721
+ "Body": {
19722
+ "type": "string",
19723
+ "example": "YES A1"
19724
+ }
19725
+ }
19726
+ }
19727
+ }
19728
+ }
19729
+ },
19730
+ "responses": {
19731
+ "200": {
19732
+ "description": "TwiML response; `<Response/>` when there is nothing to reply",
19733
+ "content": {
19734
+ "application/xml": {
19735
+ "schema": {
19736
+ "type": "string"
19737
+ }
19738
+ }
19739
+ }
19740
+ },
19741
+ "403": {
19742
+ "$ref": "#/components/responses/Forbidden"
19743
+ },
19744
+ "404": {
19745
+ "$ref": "#/components/responses/NotFound"
19746
+ }
19747
+ }
19748
+ }
19749
+ },
19750
+ "/v1/webhooks/discord/{webhook_path}": {
19751
+ "post": {
19752
+ "tags": [
19753
+ "Agent Channels"
19754
+ ],
19755
+ "summary": "Discord webhook",
19756
+ "description": "Public webhook endpoint for receiving Discord bot interactions.",
19757
+ "operationId": "discordWebhook",
19758
+ "security": [],
19759
+ "parameters": [
19760
+ {
19761
+ "name": "webhook_path",
19762
+ "in": "path",
19763
+ "required": true,
19764
+ "schema": {
19765
+ "type": "string"
19766
+ }
19767
+ }
19768
+ ],
19769
+ "requestBody": {
19770
+ "required": true,
19771
+ "content": {
19772
+ "application/json": {
19773
+ "schema": {
19774
+ "type": "object"
19775
+ }
19776
+ }
19777
+ }
19778
+ },
19779
+ "responses": {
19780
+ "200": {
19781
+ "description": "Webhook processed"
19782
+ }
19783
+ }
19784
+ }
19785
+ },
19786
+ "/v1/oauth/providers": {
19787
+ "get": {
19788
+ "tags": [
19789
+ "OAuth Connect"
19790
+ ],
19791
+ "summary": "List OAuth providers",
19792
+ "description": "Returns the list of supported OAuth providers with their metadata,\navailable scopes, and authorization URLs. No authentication required.\n",
19793
+ "operationId": "listOAuthProviders",
19794
+ "security": [],
19795
+ "responses": {
19796
+ "200": {
19797
+ "description": "Provider list",
19798
+ "content": {
19799
+ "application/json": {
19800
+ "schema": {
19801
+ "$ref": "#/components/schemas/OAuthProviderListResponse"
19802
+ }
19803
+ }
19804
+ }
19805
+ }
19806
+ }
19807
+ }
19808
+ },
19809
+ "/v1/connectors/presets": {
19810
+ "get": {
19811
+ "tags": [
19812
+ "Connectors"
19813
+ ],
19814
+ "summary": "List connector presets",
19815
+ "description": "The catalogue of pre-built connectors — Gmail, Slack, GitHub and the rest.\nEach preset carries the OAuth provider and scopes to request, plus the\nbinding config and host/path guardrails the agent will execute under.\n\nNo authentication required: this describes what 1Claw supports, not\nanything belonging to an organisation.\n",
19816
+ "operationId": "listConnectorPresets",
19817
+ "security": [],
19818
+ "responses": {
19819
+ "200": {
19820
+ "description": "Preset catalogue",
19821
+ "content": {
19822
+ "application/json": {
19823
+ "schema": {
19824
+ "type": "object",
19825
+ "properties": {
19826
+ "presets": {
19827
+ "type": "array",
19828
+ "items": {
19829
+ "$ref": "#/components/schemas/ConnectorPreset"
19830
+ }
19831
+ }
19832
+ }
19833
+ }
19834
+ }
19835
+ }
19836
+ }
19837
+ }
19838
+ }
19839
+ },
19840
+ "/v1/agents/{agent_id}/connectors": {
19841
+ "get": {
19842
+ "tags": [
19843
+ "Connectors"
19844
+ ],
19845
+ "summary": "List installed connectors",
19846
+ "description": "Connectors installed on this agent, and whether each one has actually\nbeen connected — an install creates the binding, but the binding is not\nusable until the OAuth round trip completes.\n",
19847
+ "operationId": "listInstalledConnectors",
19848
+ "parameters": [
19849
+ {
19850
+ "name": "agent_id",
19851
+ "in": "path",
19852
+ "required": true,
19853
+ "schema": {
19854
+ "type": "string",
19855
+ "format": "uuid"
19856
+ }
19857
+ }
19858
+ ],
19859
+ "responses": {
19860
+ "200": {
19861
+ "description": "Installed connectors",
19862
+ "content": {
19863
+ "application/json": {
19864
+ "schema": {
19865
+ "type": "object",
19866
+ "properties": {
19867
+ "connectors": {
19868
+ "type": "array",
19869
+ "items": {
19870
+ "$ref": "#/components/schemas/InstalledConnector"
19871
+ }
19872
+ }
19873
+ }
19874
+ }
19875
+ }
19876
+ }
19877
+ },
19878
+ "403": {
19879
+ "$ref": "#/components/responses/Forbidden"
19880
+ },
19881
+ "404": {
19882
+ "$ref": "#/components/responses/NotFound"
19883
+ }
19884
+ }
19885
+ }
19886
+ },
19887
+ "/v1/agents/{agent_id}/connectors/{slug}/install": {
19888
+ "post": {
19889
+ "tags": [
19890
+ "Connectors"
19891
+ ],
19892
+ "summary": "Install a connector",
19893
+ "description": "Creates a binding from the preset — base URL, allowed hosts, allowed\npaths — and starts the OAuth flow for it. Send the user to the returned\n`authorization_url` to finish; until they do, the binding exists but\nholds no credential.\n\nHuman users only. Installing gives an agent reach into a third-party\naccount, and the flow it starts is a person's browser.\n\nIdempotent by binding name: re-installing re-runs the OAuth flow against\nthe existing binding rather than creating a second one holding a second\ntoken for the same account. A name already taken by a binding that is\nnot this connector returns 409.\n",
19894
+ "operationId": "installConnector",
19895
+ "parameters": [
19896
+ {
19897
+ "name": "agent_id",
19898
+ "in": "path",
19899
+ "required": true,
19900
+ "schema": {
19901
+ "type": "string",
19902
+ "format": "uuid"
19903
+ }
19904
+ },
19905
+ {
19906
+ "name": "slug",
19907
+ "in": "path",
19908
+ "required": true,
19909
+ "description": "Connector preset slug, e.g. `gmail`.",
19910
+ "schema": {
19911
+ "type": "string"
19912
+ }
19913
+ }
19914
+ ],
19915
+ "requestBody": {
19916
+ "required": true,
19917
+ "content": {
19918
+ "application/json": {
19919
+ "schema": {
19920
+ "type": "object",
19921
+ "properties": {
19922
+ "binding_name": {
19923
+ "type": "string",
19924
+ "description": "Defaults to the preset slug."
19925
+ },
19926
+ "scopes": {
19927
+ "type": "array",
19928
+ "items": {
19929
+ "type": "string"
19930
+ },
19931
+ "description": "Narrow the preset's scopes. Widening is refused — the\npreset's scope list is the reviewed part of a one-click\ninstall. Must still include the preset's required scopes.\n"
19932
+ },
19933
+ "redirect_after": {
19934
+ "type": "string",
19935
+ "description": "Where to send the user after the OAuth round trip."
19936
+ }
19937
+ }
19938
+ }
19939
+ }
19940
+ }
19941
+ },
19942
+ "responses": {
19943
+ "201": {
19944
+ "description": "Connector installed; OAuth may still be pending",
19945
+ "content": {
19946
+ "application/json": {
19947
+ "schema": {
19948
+ "type": "object",
19949
+ "required": [
19950
+ "binding_id",
19951
+ "binding_name",
19952
+ "preset_slug",
19953
+ "next_step"
19954
+ ],
19955
+ "properties": {
19956
+ "binding_id": {
19957
+ "type": "string",
19958
+ "format": "uuid"
19959
+ },
19960
+ "binding_name": {
19961
+ "type": "string"
19962
+ },
19963
+ "preset_slug": {
19964
+ "type": "string"
19965
+ },
19966
+ "authorization_url": {
19967
+ "type": "string",
19968
+ "nullable": true,
19969
+ "description": "Absent for connectors that use a pasted API key rather than OAuth."
19970
+ },
19971
+ "next_step": {
19972
+ "type": "string",
19973
+ "description": "What the user still has to do, in words."
19974
+ }
19975
+ }
19976
+ }
19977
+ }
19978
+ }
19979
+ },
19980
+ "400": {
19981
+ "$ref": "#/components/responses/BadRequest"
19982
+ },
19983
+ "403": {
19984
+ "$ref": "#/components/responses/Forbidden"
19985
+ },
19986
+ "404": {
19987
+ "$ref": "#/components/responses/NotFound"
19988
+ },
19989
+ "409": {
19990
+ "$ref": "#/components/responses/Conflict"
19991
+ }
19992
+ }
19993
+ }
19994
+ },
19995
+ "/v1/agents/{agent_id}/oauth/connect": {
19996
+ "post": {
19997
+ "tags": [
19998
+ "OAuth Connect"
19999
+ ],
20000
+ "summary": "Initiate OAuth connection",
20001
+ "description": "Start an OAuth authorization flow for the specified agent and provider.\nReturns the authorization URL to redirect the user to. Human-only.\n",
20002
+ "operationId": "connectOAuth",
20003
+ "parameters": [
20004
+ {
20005
+ "$ref": "#/components/parameters/AgentId"
20006
+ }
20007
+ ],
20008
+ "requestBody": {
20009
+ "required": true,
20010
+ "content": {
20011
+ "application/json": {
20012
+ "schema": {
20013
+ "$ref": "#/components/schemas/ConnectOAuthRequest"
20014
+ }
20015
+ }
20016
+ }
20017
+ },
20018
+ "responses": {
20019
+ "200": {
20020
+ "description": "Authorization URL generated",
20021
+ "content": {
20022
+ "application/json": {
20023
+ "schema": {
20024
+ "$ref": "#/components/schemas/ConnectOAuthResponse"
20025
+ }
20026
+ }
20027
+ }
20028
+ },
20029
+ "400": {
20030
+ "$ref": "#/components/responses/BadRequest"
20031
+ },
20032
+ "403": {
20033
+ "$ref": "#/components/responses/Forbidden"
20034
+ },
20035
+ "404": {
20036
+ "$ref": "#/components/responses/NotFound"
20037
+ }
20038
+ }
20039
+ }
20040
+ },
20041
+ "/v1/agents/{agent_id}/oauth/connections": {
20042
+ "get": {
20043
+ "tags": [
20044
+ "OAuth Connect"
20045
+ ],
20046
+ "summary": "List OAuth connections",
17558
20047
  "description": "List all active OAuth connections for the specified agent.",
17559
20048
  "operationId": "listOAuthConnections",
17560
20049
  "parameters": [
@@ -19570,58 +22059,11 @@
19570
22059
  "/v1/auth/credential-recovery/requests/{id}/approve": {
19571
22060
  "post": {
19572
22061
  "tags": [
19573
- "Credential Recovery"
19574
- ],
19575
- "summary": "Approve a recovery request",
19576
- "description": "Admin/owner approves a pending recovery request. May return a one-time recovery code.",
19577
- "operationId": "approveCredentialRecovery",
19578
- "security": [
19579
- {
19580
- "BearerAuth": []
19581
- }
19582
- ],
19583
- "parameters": [
19584
- {
19585
- "name": "id",
19586
- "in": "path",
19587
- "required": true,
19588
- "schema": {
19589
- "type": "string",
19590
- "format": "uuid"
19591
- }
19592
- }
19593
- ],
19594
- "responses": {
19595
- "200": {
19596
- "description": "Request approved",
19597
- "content": {
19598
- "application/json": {
19599
- "schema": {
19600
- "$ref": "#/components/schemas/CredentialRecoveryApproveResponse"
19601
- }
19602
- }
19603
- }
19604
- },
19605
- "401": {
19606
- "$ref": "#/components/responses/Unauthorized"
19607
- },
19608
- "403": {
19609
- "$ref": "#/components/responses/Forbidden"
19610
- },
19611
- "404": {
19612
- "$ref": "#/components/responses/NotFound"
19613
- }
19614
- }
19615
- }
19616
- },
19617
- "/v1/auth/credential-recovery/requests/{id}/execute": {
19618
- "post": {
19619
- "tags": [
19620
- "Credential Recovery"
19621
- ],
19622
- "summary": "Execute an approved credential recovery request",
19623
- "description": "Execute an approved credential recovery request after the delay window has elapsed. Only org owners or admins can execute.",
19624
- "operationId": "executeCredentialRecovery",
22062
+ "Credential Recovery"
22063
+ ],
22064
+ "summary": "Approve a recovery request",
22065
+ "description": "Admin/owner approves a pending recovery request. May return a one-time recovery code.",
22066
+ "operationId": "approveCredentialRecovery",
19625
22067
  "security": [
19626
22068
  {
19627
22069
  "BearerAuth": []
@@ -19640,11 +22082,11 @@
19640
22082
  ],
19641
22083
  "responses": {
19642
22084
  "200": {
19643
- "description": "Recovery executed",
22085
+ "description": "Request approved",
19644
22086
  "content": {
19645
22087
  "application/json": {
19646
22088
  "schema": {
19647
- "$ref": "#/components/schemas/CredentialRecoveryExecuteResponse"
22089
+ "$ref": "#/components/schemas/CredentialRecoveryApproveResponse"
19648
22090
  }
19649
22091
  }
19650
22092
  }
@@ -19661,13 +22103,14 @@
19661
22103
  }
19662
22104
  }
19663
22105
  },
19664
- "/v1/auth/credential-recovery/requests/{id}": {
19665
- "delete": {
22106
+ "/v1/auth/credential-recovery/requests/{id}/execute": {
22107
+ "post": {
19666
22108
  "tags": [
19667
22109
  "Credential Recovery"
19668
22110
  ],
19669
- "summary": "Cancel or reject a recovery request",
19670
- "operationId": "cancelCredentialRecovery",
22111
+ "summary": "Execute an approved credential recovery request",
22112
+ "description": "Execute an approved credential recovery request after the delay window has elapsed. Only org owners or admins can execute.",
22113
+ "operationId": "executeCredentialRecovery",
19671
22114
  "security": [
19672
22115
  {
19673
22116
  "BearerAuth": []
@@ -19685,8 +22128,15 @@
19685
22128
  }
19686
22129
  ],
19687
22130
  "responses": {
19688
- "204": {
19689
- "description": "Request cancelled"
22131
+ "200": {
22132
+ "description": "Recovery executed",
22133
+ "content": {
22134
+ "application/json": {
22135
+ "schema": {
22136
+ "$ref": "#/components/schemas/CredentialRecoveryExecuteResponse"
22137
+ }
22138
+ }
22139
+ }
19690
22140
  },
19691
22141
  "401": {
19692
22142
  "$ref": "#/components/responses/Unauthorized"
@@ -22204,6 +24654,99 @@
22204
24654
  "description": "That label is already pinned to a different key"
22205
24655
  }
22206
24656
  }
24657
+ },
24658
+ "get": {
24659
+ "tags": [
24660
+ "Browser Bridge"
24661
+ ],
24662
+ "summary": "List the browser bridges paired to this organization. Revoked devices are included: 'was this machine ever paired' is the question asked after a laptop goes missing.",
24663
+ "operationId": "list_browser_devices",
24664
+ "responses": {
24665
+ "200": {
24666
+ "description": "The paired devices. No credential material is returned — the row holds an Argon2 hash and a prefix, and neither is exposed.",
24667
+ "content": {
24668
+ "application/json": {
24669
+ "schema": {
24670
+ "type": "object",
24671
+ "properties": {
24672
+ "devices": {
24673
+ "type": "array",
24674
+ "items": {
24675
+ "type": "object",
24676
+ "properties": {
24677
+ "id": {
24678
+ "type": "string",
24679
+ "format": "uuid"
24680
+ },
24681
+ "label": {
24682
+ "type": "string"
24683
+ },
24684
+ "platform": {
24685
+ "type": "string",
24686
+ "nullable": true
24687
+ },
24688
+ "bridge_version": {
24689
+ "type": "string",
24690
+ "nullable": true
24691
+ },
24692
+ "last_seen_at": {
24693
+ "type": "string",
24694
+ "format": "date-time",
24695
+ "nullable": true
24696
+ },
24697
+ "revoked_at": {
24698
+ "type": "string",
24699
+ "format": "date-time",
24700
+ "nullable": true,
24701
+ "description": "Set once revoked. A revoked credential stops resolving."
24702
+ },
24703
+ "created_at": {
24704
+ "type": "string",
24705
+ "format": "date-time"
24706
+ }
24707
+ }
24708
+ }
24709
+ }
24710
+ }
24711
+ }
24712
+ }
24713
+ }
24714
+ },
24715
+ "401": {
24716
+ "description": "Unauthenticated"
24717
+ }
24718
+ }
24719
+ }
24720
+ },
24721
+ "/v1/browser/devices/{id}": {
24722
+ "delete": {
24723
+ "tags": [
24724
+ "Browser Bridge"
24725
+ ],
24726
+ "summary": "Revoke a paired bridge. This is what makes a leaked bb_ credential stop working.",
24727
+ "operationId": "revoke_browser_device",
24728
+ "parameters": [
24729
+ {
24730
+ "name": "id",
24731
+ "in": "path",
24732
+ "required": true,
24733
+ "schema": {
24734
+ "type": "string",
24735
+ "format": "uuid"
24736
+ }
24737
+ }
24738
+ ],
24739
+ "responses": {
24740
+ "204": {
24741
+ "description": "Revoked. Subsequent use of that credential resolves to nothing."
24742
+ },
24743
+ "401": {
24744
+ "description": "Unauthenticated"
24745
+ },
24746
+ "404": {
24747
+ "description": "No such device in this organization"
24748
+ }
24749
+ }
22207
24750
  }
22208
24751
  },
22209
24752
  "/v1/browser/credentials": {
@@ -22249,7 +24792,7 @@
22249
24792
  "items": {
22250
24793
  "type": "string"
22251
24794
  },
22252
- "description": "Bare hostnames, compared exactly. URLs, ports and wildcards are refused: a wildcard would match nothing while looking like it allowed something."
24795
+ "description": "Hostnames. A bare entry matches only itself; a leading dot ('.example.com') matches that host and any subdomain. URLs, ports and '*' are refused '*' has no meaning to the matcher, so it would be stored and then match nothing."
22253
24796
  },
22254
24797
  "sso_hosts": {
22255
24798
  "type": "array",
@@ -22475,7 +25018,11 @@
22475
25018
  "tab_origin",
22476
25019
  "frame_origin",
22477
25020
  "frame_id",
22478
- "generation"
25021
+ "generation",
25022
+ "form_path",
25023
+ "field_names",
25024
+ "redirect_chain",
25025
+ "current_generation"
22479
25026
  ],
22480
25027
  "properties": {
22481
25028
  "session_id": {
@@ -22498,7 +25045,30 @@
22498
25045
  },
22499
25046
  "form_action_origin": {
22500
25047
  "type": "string",
22501
- "description": "Where the form would POST. Checked as well as the two origins — a login form on an allowed page can still submit to somebody else's host."
25048
+ "description": "Where the form would POST. Checked as well as the two origins — a login form on an allowed page can still submit to somebody else's host. Absent is not treated as 'same as the tab'; it denies."
25049
+ },
25050
+ "form_path": {
25051
+ "type": "string",
25052
+ "description": "Path of the form being filled, checked against the binding's fingerprint."
25053
+ },
25054
+ "field_names": {
25055
+ "type": "array",
25056
+ "items": {
25057
+ "type": "string"
25058
+ },
25059
+ "description": "Field names on that form. Every field the fingerprint expects must be present; extra fields are fine, since sites add hidden inputs."
25060
+ },
25061
+ "redirect_chain": {
25062
+ "type": "array",
25063
+ "items": {
25064
+ "type": "string"
25065
+ },
25066
+ "description": "Hosts the login has redirected through, in order. Each is checked against the binding's allowed hosts union its sso hosts."
25067
+ },
25068
+ "current_generation": {
25069
+ "type": "integer",
25070
+ "format": "int64",
25071
+ "description": "The target's generation now. A mismatch with `generation` means the page moved and the fill is denied."
22502
25072
  },
22503
25073
  "tab_origin": {
22504
25074
  "type": "string",
@@ -23769,7 +26339,10 @@
23769
26339
  ],
23770
26340
  "properties": {
23771
26341
  "name": {
23772
- "type": "string"
26342
+ "type": "string",
26343
+ "minLength": 1,
26344
+ "maxLength": 255,
26345
+ "description": "Unique within the organization. Trimmed before it is stored, and the trimmed value is what must be unique. Counted in characters, not bytes."
23773
26346
  },
23774
26347
  "description": {
23775
26348
  "type": "string"
@@ -24598,6 +27171,12 @@
24598
27171
  "nullable": true,
24599
27172
  "description": "Graduated transaction approval policy (HITL thresholds). Separate from hard guardrails."
24600
27173
  },
27174
+ "action_approval_policy": {
27175
+ "type": "object",
27176
+ "additionalProperties": true,
27177
+ "nullable": true,
27178
+ "description": "Which business actions this agent must ask a human about, and above what\namount. `{}` means no per-action rules.\n\nShape: `{ \"default_mode\": \"deny|approve|allow\", \"rules\": [ { \"action_type\":\n\"refund.create\", \"mode\": \"approve\", \"require_for_amount_above_usd\": \"50\",\n\"summary_template\": \"Refund {{amount_usd}} to {{customer_email}}\" } ] }`.\n\nA rule can only raise the bar. Editing this is classified as a guardrail\nwidening, so it routes through the same approval flow as loosening a\ntransaction limit. Malformed rules are rejected on write rather than\nignored at request time.\n"
27179
+ },
24601
27180
  "typed_data_policy": {
24602
27181
  "type": "string",
24603
27182
  "enum": [
@@ -24843,6 +27422,12 @@
24843
27422
  "nullable": true,
24844
27423
  "description": "Graduated transaction approval policy (HITL thresholds)."
24845
27424
  },
27425
+ "action_approval_policy": {
27426
+ "type": "object",
27427
+ "additionalProperties": true,
27428
+ "nullable": true,
27429
+ "description": "Which business actions this agent must ask a human about, and above what\namount. `{}` means no per-action rules.\n\nShape: `{ \"default_mode\": \"deny|approve|allow\", \"rules\": [ { \"action_type\":\n\"refund.create\", \"mode\": \"approve\", \"require_for_amount_above_usd\": \"50\",\n\"summary_template\": \"Refund {{amount_usd}} to {{customer_email}}\" } ] }`.\n\nA rule can only raise the bar. Editing this is classified as a guardrail\nwidening, so it routes through the same approval flow as loosening a\ntransaction limit. Malformed rules are rejected on write rather than\nignored at request time.\n"
27430
+ },
24846
27431
  "typed_data_policy": {
24847
27432
  "type": "string",
24848
27433
  "enum": [
@@ -25188,6 +27773,12 @@
25188
27773
  "nullable": true,
25189
27774
  "description": "Graduated transaction approval policy (HITL thresholds)."
25190
27775
  },
27776
+ "action_approval_policy": {
27777
+ "type": "object",
27778
+ "additionalProperties": true,
27779
+ "nullable": true,
27780
+ "description": "Which business actions this agent must ask a human about, and above what\namount. `{}` means no per-action rules.\n\nShape: `{ \"default_mode\": \"deny|approve|allow\", \"rules\": [ { \"action_type\":\n\"refund.create\", \"mode\": \"approve\", \"require_for_amount_above_usd\": \"50\",\n\"summary_template\": \"Refund {{amount_usd}} to {{customer_email}}\" } ] }`.\n\nA rule can only raise the bar. Editing this is classified as a guardrail\nwidening, so it routes through the same approval flow as loosening a\ntransaction limit. Malformed rules are rejected on write rather than\nignored at request time.\n"
27781
+ },
25191
27782
  "typed_data_policy": {
25192
27783
  "type": "string",
25193
27784
  "enum": [
@@ -30087,39 +32678,240 @@
30087
32678
  }
30088
32679
  }
30089
32680
  },
30090
- "PlatformTemplateResponse": {
32681
+ "PlatformTemplateResponse": {
32682
+ "type": "object",
32683
+ "properties": {
32684
+ "id": {
32685
+ "type": "string",
32686
+ "format": "uuid"
32687
+ },
32688
+ "platform_app_id": {
32689
+ "type": "string",
32690
+ "format": "uuid"
32691
+ },
32692
+ "name": {
32693
+ "type": "string"
32694
+ },
32695
+ "description": {
32696
+ "type": "string"
32697
+ },
32698
+ "version": {
32699
+ "type": "integer"
32700
+ },
32701
+ "spec": {
32702
+ "type": "object"
32703
+ },
32704
+ "is_active": {
32705
+ "type": "boolean"
32706
+ },
32707
+ "created_at": {
32708
+ "type": "string",
32709
+ "format": "date-time"
32710
+ },
32711
+ "updated_at": {
32712
+ "type": "string",
32713
+ "format": "date-time"
32714
+ }
32715
+ }
32716
+ },
32717
+ "FleetSummaryResponse": {
32718
+ "type": "object",
32719
+ "properties": {
32720
+ "template_id": {
32721
+ "type": "string",
32722
+ "format": "uuid"
32723
+ },
32724
+ "template_name": {
32725
+ "type": "string"
32726
+ },
32727
+ "current_version": {
32728
+ "type": "integer"
32729
+ },
32730
+ "spec_hash": {
32731
+ "type": [
32732
+ "string",
32733
+ "null"
32734
+ ],
32735
+ "description": "SHA-256 of the template spec. Lets a caller tell a version bump that changed nothing from one that did. Null on templates written before migration 245.\n"
32736
+ },
32737
+ "total_agents": {
32738
+ "type": "integer",
32739
+ "format": "int64"
32740
+ },
32741
+ "version_skew": {
32742
+ "type": "array",
32743
+ "description": "How the cohort splits across the versions it was provisioned from.",
32744
+ "items": {
32745
+ "type": "object",
32746
+ "properties": {
32747
+ "template_version": {
32748
+ "type": [
32749
+ "integer",
32750
+ "null"
32751
+ ]
32752
+ },
32753
+ "agents": {
32754
+ "type": "integer",
32755
+ "format": "int64"
32756
+ }
32757
+ }
32758
+ }
32759
+ },
32760
+ "agents_on_current_version": {
32761
+ "type": "integer",
32762
+ "format": "int64"
32763
+ },
32764
+ "agents_behind": {
32765
+ "type": "integer",
32766
+ "format": "int64"
32767
+ },
32768
+ "drifted_agents": {
32769
+ "type": "integer",
32770
+ "format": "int64",
32771
+ "description": "Agents a previous rollout declined to touch."
32772
+ },
32773
+ "bulk_patchable_fields": {
32774
+ "type": "array",
32775
+ "description": "The fields bulk-patch and rollout will carry. Read this rather than hard-coding the list; it is deliberately narrower than a single-agent PATCH and may narrow further.\n",
32776
+ "items": {
32777
+ "type": "string"
32778
+ }
32779
+ }
32780
+ }
32781
+ },
32782
+ "ListFleetAgentsResponse": {
32783
+ "type": "object",
32784
+ "properties": {
32785
+ "agents": {
32786
+ "type": "array",
32787
+ "items": {
32788
+ "$ref": "#/components/schemas/FleetAgent"
32789
+ }
32790
+ },
32791
+ "limit": {
32792
+ "type": "integer"
32793
+ },
32794
+ "offset": {
32795
+ "type": "integer"
32796
+ },
32797
+ "current_version": {
32798
+ "type": "integer"
32799
+ }
32800
+ }
32801
+ },
32802
+ "FleetAgent": {
32803
+ "type": "object",
32804
+ "properties": {
32805
+ "agent_id": {
32806
+ "type": "string",
32807
+ "format": "uuid"
32808
+ },
32809
+ "name": {
32810
+ "type": "string"
32811
+ },
32812
+ "org_id": {
32813
+ "type": "string",
32814
+ "format": "uuid"
32815
+ },
32816
+ "platform_connection_id": {
32817
+ "type": [
32818
+ "string",
32819
+ "null"
32820
+ ],
32821
+ "format": "uuid"
32822
+ },
32823
+ "provisioned_from_version": {
32824
+ "type": [
32825
+ "integer",
32826
+ "null"
32827
+ ]
32828
+ },
32829
+ "last_fleet_sync_at": {
32830
+ "type": [
32831
+ "string",
32832
+ "null"
32833
+ ],
32834
+ "format": "date-time"
32835
+ },
32836
+ "drift_fields": {
32837
+ "type": "array",
32838
+ "description": "Fields a rollout skipped because they were changed outside fleet control. The standing answer to \"why is this agent behind?\".\n",
32839
+ "items": {
32840
+ "type": "string"
32841
+ }
32842
+ },
32843
+ "is_active": {
32844
+ "type": "boolean"
32845
+ },
32846
+ "is_current": {
32847
+ "type": "boolean"
32848
+ }
32849
+ }
32850
+ },
32851
+ "FleetRolloutResponse": {
30091
32852
  "type": "object",
30092
32853
  "properties": {
30093
- "id": {
30094
- "type": "string",
30095
- "format": "uuid"
32854
+ "job_id": {
32855
+ "type": [
32856
+ "string",
32857
+ "null"
32858
+ ],
32859
+ "format": "uuid",
32860
+ "description": "Null for a dry run, which claims no job."
30096
32861
  },
30097
- "platform_app_id": {
30098
- "type": "string",
30099
- "format": "uuid"
32862
+ "to_version": {
32863
+ "type": "integer"
30100
32864
  },
30101
- "name": {
30102
- "type": "string"
32865
+ "dry_run": {
32866
+ "type": "boolean"
30103
32867
  },
30104
- "description": {
30105
- "type": "string"
32868
+ "forced": {
32869
+ "type": "boolean"
30106
32870
  },
30107
- "version": {
30108
- "type": "integer"
32871
+ "total_agents": {
32872
+ "type": "integer",
32873
+ "format": "int64"
30109
32874
  },
30110
- "spec": {
30111
- "type": "object"
32875
+ "synced": {
32876
+ "type": "integer"
30112
32877
  },
30113
- "is_active": {
30114
- "type": "boolean"
32878
+ "already_current": {
32879
+ "type": "integer"
30115
32880
  },
30116
- "created_at": {
30117
- "type": "string",
30118
- "format": "date-time"
32881
+ "skipped_drifted": {
32882
+ "type": "integer"
30119
32883
  },
30120
- "updated_at": {
30121
- "type": "string",
30122
- "format": "date-time"
32884
+ "outcomes": {
32885
+ "type": "array",
32886
+ "items": {
32887
+ "type": "object",
32888
+ "properties": {
32889
+ "outcome": {
32890
+ "type": "string",
32891
+ "enum": [
32892
+ "already_current",
32893
+ "synced",
32894
+ "skipped_drifted"
32895
+ ]
32896
+ },
32897
+ "agent_id": {
32898
+ "type": "string",
32899
+ "format": "uuid"
32900
+ },
32901
+ "fields": {
32902
+ "type": "array",
32903
+ "items": {
32904
+ "type": "string"
32905
+ }
32906
+ },
32907
+ "drift_fields": {
32908
+ "type": "array",
32909
+ "items": {
32910
+ "type": "string"
32911
+ }
32912
+ }
32913
+ }
32914
+ }
30123
32915
  }
30124
32916
  }
30125
32917
  },
@@ -30784,13 +33576,277 @@
30784
33576
  }
30785
33577
  }
30786
33578
  },
30787
- "DeviceResponse": {
33579
+ "DeviceResponse": {
33580
+ "type": "object",
33581
+ "required": [
33582
+ "id",
33583
+ "name",
33584
+ "platform",
33585
+ "attestation_verified",
33586
+ "created_at"
33587
+ ],
33588
+ "properties": {
33589
+ "id": {
33590
+ "type": "string",
33591
+ "format": "uuid"
33592
+ },
33593
+ "name": {
33594
+ "type": "string"
33595
+ },
33596
+ "platform": {
33597
+ "type": "string"
33598
+ },
33599
+ "attestation_verified": {
33600
+ "type": "boolean"
33601
+ },
33602
+ "last_used_at": {
33603
+ "type": "string",
33604
+ "format": "date-time",
33605
+ "nullable": true
33606
+ },
33607
+ "created_at": {
33608
+ "type": "string",
33609
+ "format": "date-time"
33610
+ }
33611
+ }
33612
+ },
33613
+ "CreateDeviceChallengeRequest": {
33614
+ "type": "object",
33615
+ "required": [
33616
+ "action",
33617
+ "target_id"
33618
+ ],
33619
+ "properties": {
33620
+ "action": {
33621
+ "type": "string",
33622
+ "description": "The action this challenge authorizes (e.g. \"approve_transaction\")"
33623
+ },
33624
+ "target_id": {
33625
+ "type": "string",
33626
+ "description": "ID of the resource the action targets"
33627
+ }
33628
+ }
33629
+ },
33630
+ "DeviceChallengeResponse": {
33631
+ "type": "object",
33632
+ "required": [
33633
+ "challenge_nonce",
33634
+ "expires_at",
33635
+ "action_bound_hash"
33636
+ ],
33637
+ "properties": {
33638
+ "challenge_nonce": {
33639
+ "type": "string"
33640
+ },
33641
+ "expires_at": {
33642
+ "type": "string",
33643
+ "format": "date-time"
33644
+ },
33645
+ "action_bound_hash": {
33646
+ "type": "string",
33647
+ "description": "SHA-256 binding the challenge to the requested action and target"
33648
+ }
33649
+ }
33650
+ },
33651
+ "AttestDeviceChallengeRequest": {
33652
+ "type": "object",
33653
+ "required": [
33654
+ "challenge_nonce",
33655
+ "signature"
33656
+ ],
33657
+ "properties": {
33658
+ "challenge_nonce": {
33659
+ "type": "string"
33660
+ },
33661
+ "signature": {
33662
+ "type": "string",
33663
+ "description": "Signature over the challenge nonce using the device's private key"
33664
+ }
33665
+ }
33666
+ },
33667
+ "AttestDeviceChallengeResponse": {
33668
+ "type": "object",
33669
+ "required": [
33670
+ "step_up_token",
33671
+ "expires_at"
33672
+ ],
33673
+ "properties": {
33674
+ "step_up_token": {
33675
+ "type": "string",
33676
+ "description": "Short-lived token authorizing the bound action"
33677
+ },
33678
+ "expires_at": {
33679
+ "type": "string",
33680
+ "format": "date-time"
33681
+ }
33682
+ }
33683
+ },
33684
+ "RegisterPushTokenRequest": {
33685
+ "type": "object",
33686
+ "required": [
33687
+ "token",
33688
+ "platform"
33689
+ ],
33690
+ "properties": {
33691
+ "token": {
33692
+ "type": "string",
33693
+ "description": "Push notification token from APNs or FCM"
33694
+ },
33695
+ "platform": {
33696
+ "type": "string",
33697
+ "enum": [
33698
+ "apns",
33699
+ "fcm"
33700
+ ]
33701
+ }
33702
+ }
33703
+ },
33704
+ "DecideApprovalRequest": {
33705
+ "type": "object",
33706
+ "required": [
33707
+ "decision"
33708
+ ],
33709
+ "properties": {
33710
+ "decision": {
33711
+ "type": "string",
33712
+ "enum": [
33713
+ "approve",
33714
+ "reject"
33715
+ ]
33716
+ },
33717
+ "reason": {
33718
+ "type": "string",
33719
+ "description": "Optional human-readable reason for the decision"
33720
+ }
33721
+ }
33722
+ },
33723
+ "UsageCounts": {
33724
+ "type": "object",
33725
+ "required": [
33726
+ "api_requests",
33727
+ "signatures",
33728
+ "execution_intents",
33729
+ "execution_intents_tee",
33730
+ "inference_usd",
33731
+ "credits_debited_cents"
33732
+ ],
33733
+ "properties": {
33734
+ "api_requests": {
33735
+ "type": "integer",
33736
+ "format": "int64"
33737
+ },
33738
+ "signatures": {
33739
+ "type": "integer",
33740
+ "format": "int64"
33741
+ },
33742
+ "execution_intents": {
33743
+ "type": "integer",
33744
+ "format": "int64"
33745
+ },
33746
+ "execution_intents_tee": {
33747
+ "type": "integer",
33748
+ "format": "int64"
33749
+ },
33750
+ "inference_usd": {
33751
+ "type": "string",
33752
+ "description": "A decimal string. Money is not a float; zero is \"0\".",
33753
+ "example": "3.42"
33754
+ },
33755
+ "credits_debited_cents": {
33756
+ "type": "integer",
33757
+ "format": "int64"
33758
+ }
33759
+ }
33760
+ },
33761
+ "AppUsageReport": {
33762
+ "type": "object",
33763
+ "required": [
33764
+ "app_id",
33765
+ "period_start",
33766
+ "period_end",
33767
+ "connections",
33768
+ "unattributed",
33769
+ "totals",
33770
+ "has_ambiguous_usage"
33771
+ ],
33772
+ "properties": {
33773
+ "app_id": {
33774
+ "type": "string",
33775
+ "format": "uuid"
33776
+ },
33777
+ "period_start": {
33778
+ "type": "string",
33779
+ "format": "date-time"
33780
+ },
33781
+ "period_end": {
33782
+ "type": "string",
33783
+ "format": "date-time",
33784
+ "description": "Exclusive. The period is half-open, so an event at midnight belongs to one month, not two."
33785
+ },
33786
+ "connections": {
33787
+ "type": "array",
33788
+ "items": {
33789
+ "type": "object",
33790
+ "required": [
33791
+ "connection_id",
33792
+ "usage"
33793
+ ],
33794
+ "properties": {
33795
+ "connection_id": {
33796
+ "type": "string",
33797
+ "format": "uuid"
33798
+ },
33799
+ "usage": {
33800
+ "$ref": "#/components/schemas/UsageCounts"
33801
+ }
33802
+ }
33803
+ }
33804
+ },
33805
+ "unattributed": {
33806
+ "type": "object",
33807
+ "description": "Usage that could not be charged to a connection.",
33808
+ "properties": {
33809
+ "ambiguous": {
33810
+ "allOf": [
33811
+ {
33812
+ "$ref": "#/components/schemas/UsageCounts"
33813
+ }
33814
+ ],
33815
+ "description": "The agent belongs to several connections and none was named. This belongs to someone."
33816
+ },
33817
+ "none": {
33818
+ "allOf": [
33819
+ {
33820
+ "$ref": "#/components/schemas/UsageCounts"
33821
+ }
33822
+ ],
33823
+ "description": "No platform linkage. Normal for most traffic."
33824
+ }
33825
+ }
33826
+ },
33827
+ "totals": {
33828
+ "allOf": [
33829
+ {
33830
+ "$ref": "#/components/schemas/UsageCounts"
33831
+ }
33832
+ ],
33833
+ "description": "Connections plus both unattributed buckets. Derived, not queried."
33834
+ },
33835
+ "has_ambiguous_usage": {
33836
+ "type": "boolean",
33837
+ "description": "Some usage this period belongs to an end-user who cannot be identified."
33838
+ }
33839
+ }
33840
+ },
33841
+ "Peer": {
30788
33842
  "type": "object",
30789
33843
  "required": [
30790
33844
  "id",
30791
- "name",
30792
- "platform",
30793
- "attestation_verified",
33845
+ "peer_type",
33846
+ "peer_ref",
33847
+ "profile",
33848
+ "status",
33849
+ "observer_count",
30794
33850
  "created_at"
30795
33851
  ],
30796
33852
  "properties": {
@@ -30798,133 +33854,255 @@
30798
33854
  "type": "string",
30799
33855
  "format": "uuid"
30800
33856
  },
30801
- "name": {
30802
- "type": "string"
33857
+ "peer_type": {
33858
+ "type": "string",
33859
+ "enum": [
33860
+ "user",
33861
+ "platform_connection",
33862
+ "external"
33863
+ ]
30803
33864
  },
30804
- "platform": {
33865
+ "peer_ref": {
30805
33866
  "type": "string"
30806
33867
  },
30807
- "attestation_verified": {
30808
- "type": "boolean"
30809
- },
30810
- "last_used_at": {
33868
+ "display_name": {
30811
33869
  "type": "string",
30812
- "format": "date-time",
30813
33870
  "nullable": true
30814
33871
  },
33872
+ "profile": {
33873
+ "type": "object",
33874
+ "additionalProperties": true
33875
+ },
33876
+ "status": {
33877
+ "type": "string",
33878
+ "enum": [
33879
+ "active",
33880
+ "archived"
33881
+ ],
33882
+ "description": "Archived when a connection is disconnected. Agents lose observation;\nthe person keeps export and delete.\n"
33883
+ },
33884
+ "observer_count": {
33885
+ "type": "integer",
33886
+ "description": "How many agents observe this peer. The list itself is not returned here."
33887
+ },
30815
33888
  "created_at": {
30816
33889
  "type": "string",
30817
33890
  "format": "date-time"
30818
33891
  }
30819
33892
  }
30820
33893
  },
30821
- "CreateDeviceChallengeRequest": {
33894
+ "PeerFact": {
30822
33895
  "type": "object",
30823
33896
  "required": [
30824
- "action",
30825
- "target_id"
33897
+ "fact_key",
33898
+ "fact_value",
33899
+ "provenance",
33900
+ "edited_by_human",
33901
+ "updated_at"
30826
33902
  ],
30827
33903
  "properties": {
30828
- "action": {
33904
+ "fact_key": {
30829
33905
  "type": "string",
30830
- "description": "The action this challenge authorizes (e.g. \"approve_transaction\")"
33906
+ "example": "approval_tendency:refund.create|0-10|known|a@b.co"
30831
33907
  },
30832
- "target_id": {
33908
+ "fact_value": {
33909
+ "type": "object",
33910
+ "additionalProperties": true
33911
+ },
33912
+ "confidence": {
30833
33913
  "type": "string",
30834
- "description": "ID of the resource the action targets"
33914
+ "nullable": true,
33915
+ "description": "0..1, capped below certainty — no history makes the next decision certain."
33916
+ },
33917
+ "provenance": {
33918
+ "type": "array",
33919
+ "items": {
33920
+ "type": "object"
33921
+ },
33922
+ "description": "Why this is believed. Entries are `event`, `tombstone` (the event has\nexpired; kind and decision are kept, content is not) or `human`.\n"
33923
+ },
33924
+ "edited_by_human": {
33925
+ "type": "boolean",
33926
+ "description": "A person corrected this. The processor will not overwrite it."
33927
+ },
33928
+ "updated_at": {
33929
+ "type": "string",
33930
+ "format": "date-time"
30835
33931
  }
30836
33932
  }
30837
33933
  },
30838
- "DeviceChallengeResponse": {
33934
+ "NotificationTarget": {
30839
33935
  "type": "object",
30840
33936
  "required": [
30841
- "challenge_nonce",
30842
- "expires_at",
30843
- "action_bound_hash"
33937
+ "id",
33938
+ "target_type",
33939
+ "config",
33940
+ "events",
33941
+ "is_active",
33942
+ "verified",
33943
+ "created_at"
30844
33944
  ],
30845
33945
  "properties": {
30846
- "challenge_nonce": {
30847
- "type": "string"
33946
+ "id": {
33947
+ "type": "string",
33948
+ "format": "uuid"
30848
33949
  },
30849
- "expires_at": {
33950
+ "target_type": {
30850
33951
  "type": "string",
30851
- "format": "date-time"
33952
+ "enum": [
33953
+ "sms",
33954
+ "webhook",
33955
+ "expo",
33956
+ "email"
33957
+ ]
30852
33958
  },
30853
- "action_bound_hash": {
33959
+ "user_id": {
30854
33960
  "type": "string",
30855
- "description": "SHA-256 binding the challenge to the requested action and target"
33961
+ "format": "uuid",
33962
+ "nullable": true
33963
+ },
33964
+ "agent_id": {
33965
+ "type": "string",
33966
+ "format": "uuid",
33967
+ "nullable": true
33968
+ },
33969
+ "config": {
33970
+ "type": "object",
33971
+ "additionalProperties": true
33972
+ },
33973
+ "events": {
33974
+ "type": "array",
33975
+ "items": {
33976
+ "type": "string"
33977
+ }
33978
+ },
33979
+ "is_active": {
33980
+ "type": "boolean"
33981
+ },
33982
+ "verified": {
33983
+ "type": "boolean",
33984
+ "description": "An unverified SMS target receives notifications but cannot decide\nan approval by reply.\n"
33985
+ },
33986
+ "created_at": {
33987
+ "type": "string",
33988
+ "format": "date-time"
30856
33989
  }
30857
33990
  }
30858
33991
  },
30859
- "AttestDeviceChallengeRequest": {
33992
+ "ConnectorPreset": {
30860
33993
  "type": "object",
30861
33994
  "required": [
30862
- "challenge_nonce",
30863
- "signature"
33995
+ "slug",
33996
+ "display_name",
33997
+ "description",
33998
+ "category",
33999
+ "binding_type",
34000
+ "base_url",
34001
+ "requires_oauth"
30864
34002
  ],
30865
34003
  "properties": {
30866
- "challenge_nonce": {
34004
+ "slug": {
34005
+ "type": "string",
34006
+ "example": "gmail"
34007
+ },
34008
+ "display_name": {
34009
+ "type": "string",
34010
+ "example": "Gmail"
34011
+ },
34012
+ "description": {
30867
34013
  "type": "string"
30868
34014
  },
30869
- "signature": {
34015
+ "category": {
30870
34016
  "type": "string",
30871
- "description": "Signature over the challenge nonce using the device's private key"
30872
- }
30873
- }
30874
- },
30875
- "AttestDeviceChallengeResponse": {
30876
- "type": "object",
30877
- "required": [
30878
- "step_up_token",
30879
- "expires_at"
30880
- ],
30881
- "properties": {
30882
- "step_up_token": {
34017
+ "example": "communication"
34018
+ },
34019
+ "provider_slug": {
30883
34020
  "type": "string",
30884
- "description": "Short-lived token authorizing the bound action"
34021
+ "nullable": true,
34022
+ "description": "`oauth_providers.slug`, or null when the connector uses a pasted API key."
30885
34023
  },
30886
- "expires_at": {
34024
+ "oauth_scopes": {
34025
+ "type": "array",
34026
+ "items": {
34027
+ "type": "string"
34028
+ }
34029
+ },
34030
+ "required_scopes": {
34031
+ "type": "array",
34032
+ "items": {
34033
+ "type": "string"
34034
+ },
34035
+ "description": "Scopes without which the connector cannot do anything."
34036
+ },
34037
+ "binding_type": {
30887
34038
  "type": "string",
30888
- "format": "date-time"
30889
- }
30890
- }
30891
- },
30892
- "RegisterPushTokenRequest": {
30893
- "type": "object",
30894
- "required": [
30895
- "token",
30896
- "platform"
30897
- ],
30898
- "properties": {
30899
- "token": {
34039
+ "example": "http"
34040
+ },
34041
+ "base_url": {
30900
34042
  "type": "string",
30901
- "description": "Push notification token from APNs or FCM"
34043
+ "format": "uri"
30902
34044
  },
30903
- "platform": {
34045
+ "allowed_hosts": {
34046
+ "type": "array",
34047
+ "items": {
34048
+ "type": "string"
34049
+ },
34050
+ "description": "Hosts the installed binding may reach. Always includes the base URL's host."
34051
+ },
34052
+ "documentation_url": {
30904
34053
  "type": "string",
30905
- "enum": [
30906
- "apns",
30907
- "fcm"
30908
- ]
34054
+ "format": "uri"
34055
+ },
34056
+ "tier_required": {
34057
+ "type": "string",
34058
+ "example": "free"
34059
+ },
34060
+ "requires_oauth": {
34061
+ "type": "boolean"
30909
34062
  }
30910
34063
  }
30911
34064
  },
30912
- "DecideApprovalRequest": {
34065
+ "InstalledConnector": {
30913
34066
  "type": "object",
30914
34067
  "required": [
30915
- "decision"
34068
+ "binding_id",
34069
+ "binding_name",
34070
+ "preset_slug",
34071
+ "is_active",
34072
+ "connected",
34073
+ "needs_reauth",
34074
+ "created_at"
30916
34075
  ],
30917
34076
  "properties": {
30918
- "decision": {
34077
+ "binding_id": {
30919
34078
  "type": "string",
30920
- "enum": [
30921
- "approve",
30922
- "reject"
30923
- ]
34079
+ "format": "uuid"
30924
34080
  },
30925
- "reason": {
34081
+ "binding_name": {
34082
+ "type": "string"
34083
+ },
34084
+ "preset_slug": {
34085
+ "type": "string"
34086
+ },
34087
+ "display_name": {
30926
34088
  "type": "string",
30927
- "description": "Optional human-readable reason for the decision"
34089
+ "nullable": true,
34090
+ "description": "Null if the preset has since been retired from the catalogue."
34091
+ },
34092
+ "is_active": {
34093
+ "type": "boolean"
34094
+ },
34095
+ "connected": {
34096
+ "type": "boolean",
34097
+ "description": "The OAuth round trip completed and a token is stored."
34098
+ },
34099
+ "needs_reauth": {
34100
+ "type": "boolean",
34101
+ "description": "The stored token was rejected; the user must reconnect."
34102
+ },
34103
+ "created_at": {
34104
+ "type": "string",
34105
+ "format": "date-time"
30928
34106
  }
30929
34107
  }
30930
34108
  },
@@ -30972,7 +34150,28 @@
30972
34150
  "risk_tier": {
30973
34151
  "type": "integer",
30974
34152
  "minimum": 1,
30975
- "maximum": 3
34153
+ "maximum": 3,
34154
+ "description": "The tier actually enforced. Authoritative."
34155
+ },
34156
+ "declared_risk_tier": {
34157
+ "type": "integer",
34158
+ "minimum": 1,
34159
+ "maximum": 3,
34160
+ "nullable": true,
34161
+ "description": "What the caller asked for, when it asked for anything."
34162
+ },
34163
+ "declared_below_floor": {
34164
+ "type": "boolean",
34165
+ "description": "The caller asked for a lower tier than policy required."
34166
+ },
34167
+ "human_summary": {
34168
+ "type": "string",
34169
+ "nullable": true,
34170
+ "description": "Plain-language line sent to SMS, push and email."
34171
+ },
34172
+ "payload": {
34173
+ "type": "object",
34174
+ "description": "What the action will do, as submitted."
30976
34175
  },
30977
34176
  "status": {
30978
34177
  "type": "string",
@@ -31486,7 +34685,10 @@
31486
34685
  "required": [
31487
34686
  "connection_id",
31488
34687
  "period",
31489
- "inference_spent_usd"
34688
+ "inference_spent_usd",
34689
+ "usage",
34690
+ "period_start",
34691
+ "period_end"
31490
34692
  ],
31491
34693
  "properties": {
31492
34694
  "connection_id": {
@@ -31498,7 +34700,25 @@
31498
34700
  "description": "UTC month (YYYY-MM)"
31499
34701
  },
31500
34702
  "inference_spent_usd": {
31501
- "type": "string"
34703
+ "type": "string",
34704
+ "description": "Kept for compatibility — this field predates the breakdown below and\nexisting integrations read it. Same number as `usage.inference_usd`.\n"
34705
+ },
34706
+ "usage": {
34707
+ "allOf": [
34708
+ {
34709
+ "$ref": "#/components/schemas/UsageCounts"
34710
+ }
34711
+ ],
34712
+ "description": "Everything billable for this connection in the period. Derived from the\nsame grouped query as the app report, so a connection can never report a\nnumber the app report disagrees with.\n"
34713
+ },
34714
+ "period_start": {
34715
+ "type": "string",
34716
+ "format": "date-time"
34717
+ },
34718
+ "period_end": {
34719
+ "type": "string",
34720
+ "format": "date-time",
34721
+ "description": "Exclusive. The period is half-open."
31502
34722
  }
31503
34723
  }
31504
34724
  },