@primitivedotdev/sdk 1.29.0 → 1.31.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.
@@ -112,6 +112,10 @@ const openapiDocument = {
112
112
  {
113
113
  "name": "Registries",
114
114
  "description": "The Agent Registry: ownable directories of agents, addressable by a\nregistry-scoped handle. A registry's publish policy (owner_only, request,\nor open) decides whether a publish lists immediately or pends owner\napproval. An agent is defined once with a globally unique,\nreachability-verified address, then published into any registry under a\nhandle. Discovery reads (list, resolve, get) are public for public\nregistries; managing a registry and moderating requests use the owner's\nAPI key.\n"
115
+ },
116
+ {
117
+ "name": "Contacts",
118
+ "description": "Organization contacts and per-address agent preferences."
115
119
  }
116
120
  ],
117
121
  "paths": {
@@ -1080,7 +1084,7 @@ const openapiDocument = {
1080
1084
  "/emails/search": { "get": {
1081
1085
  "operationId": "searchEmails",
1082
1086
  "summary": "Search inbound emails",
1083
- "description": "Searches inbound emails with structured filters and optional\nfull-text matching across parsed email fields. This endpoint is\noptimized for filtered inbox views and CLI polling workflows:\ncallers that only need new accepted mail can pass\n`sort=received_at_asc`, `snippet=false`, `include_facets=false`,\nand a `date_from` timestamp.\n\n`q`, `subject`, and `body` use the same English full-text index\nas the web inbox search. Structured filters such as `from`, `to`,\n`domain_id`, status, attachment presence, and spam score bounds\nare combined with the text query.\n",
1087
+ "description": "Searches inbound emails with structured filters and optional\nfull-text matching across parsed email fields. This endpoint is\noptimized for filtered inbox views and CLI polling workflows:\ncallers that only need new accepted mail can pass\n`sort=received_at_asc`, `snippet=false`, `include_facets=false`,\nand a `date_from` timestamp.\n\n`q`, `subject`, and `body` use the same English full-text index\nas the web inbox search. Structured filters such as `from`, `to`,\n`domain_id`, status, attachment presence, and spam score bounds\nare combined with the text query.\n\nConnected-agent credentials search only mail received by their own\naddress. This applies to results, totals, facets, and every page;\nsearch filters cannot widen the credential's scope. When\n`reply_to_sent_email_id` is supplied, its parent send must belong\nto the connected address in the same organization. An unavailable\nparent returns 404. Sender filters are not authentication proof;\ninspect the email detail's authentication evidence before trusting it.\n",
1084
1088
  "tags": ["Emails"],
1085
1089
  "parameters": [
1086
1090
  {
@@ -1253,6 +1257,7 @@ const openapiDocument = {
1253
1257
  },
1254
1258
  "400": { "$ref": "#/components/responses/ValidationError" },
1255
1259
  "401": { "$ref": "#/components/responses/Unauthorized" },
1260
+ "404": { "$ref": "#/components/responses/NotFound" },
1256
1261
  "504": {
1257
1262
  "description": "Search query timed out",
1258
1263
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
@@ -4258,62 +4263,25 @@ const openapiDocument = {
4258
4263
  "404": { "$ref": "#/components/responses/NotFound" }
4259
4264
  }
4260
4265
  }
4261
- }
4262
- },
4263
- "components": {
4264
- "securitySchemes": {
4265
- "BearerAuth": {
4266
- "type": "http",
4267
- "scheme": "bearer",
4268
- "description": "API key with `prim_` prefix: `Authorization: Bearer prim_<key>`"
4269
- },
4270
- "DownloadToken": {
4271
- "type": "apiKey",
4272
- "in": "query",
4273
- "name": "token",
4274
- "description": "Signed download token provided in webhook payloads"
4275
- }
4276
4266
  },
4277
- "parameters": {
4278
- "AttachmentPartIndex": {
4279
- "name": "part_index",
4280
- "in": "path",
4281
- "required": true,
4282
- "description": "The attachment metadata `part_index`, not its offset in the attachments array",
4283
- "schema": {
4284
- "type": "integer",
4285
- "format": "int32",
4286
- "minimum": 0,
4287
- "maximum": 2147483647
4288
- }
4289
- },
4290
- "ResourceId": {
4291
- "name": "id",
4292
- "in": "path",
4293
- "required": true,
4267
+ "/contacts": { "get": {
4268
+ "operationId": "listContacts",
4269
+ "summary": "list Contacts",
4270
+ "tags": ["Contacts"],
4271
+ "security": [{ "BearerAuth": [] }],
4272
+ "description": "Organization directory and agent preferences; no profiles, message history or runtime presence. Existing organization credentials use tenant permissions. Connected keys may read the directory, create address-only missing contacts using if_absent:true, and access only their own agent memberships; they cannot edit shared labels or delete directory entries. Function credentials are not granted access. Memberships require an existing local agent connection (including revoked connections) and an existing contact. PUT requires exactly one precondition. if_absent returns an existing row unchanged when supplied fields agree; otherwise 409. notify defaults false. Off-to-on generates a new server timestamp and generation; on-to-on and purpose-only edits preserve them. Disable clears activation metadata. Deletion atomically removes membership eligibility. Receivers must recheck generation/preferences before dispatch and pause admission when their policy cache is older than 30 seconds; explicit reply waits are independent. DELETE requires if_version, returns deleted:false for an absent row, and rejects stale versions of recreated rows. Pages use ascending canonical address, with meta.cursor null on the last page. Lists are live pages, not snapshots.",
4273
+ "parameters": [{
4274
+ "name": "cursor",
4275
+ "in": "query",
4294
4276
  "schema": {
4295
4277
  "type": "string",
4296
- "format": "uuid"
4278
+ "format": "email",
4279
+ "maxLength": 254,
4280
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
4297
4281
  },
4298
- "description": "Resource UUID"
4299
- },
4300
- "IdempotencyKey": {
4301
- "name": "idempotency-key",
4302
- "in": "header",
4303
4282
  "required": false,
4304
- "schema": {
4305
- "type": "string",
4306
- "maxLength": 255
4307
- },
4308
- "description": "Optional idempotency key. Retrying a request with the same key returns\nthe original result instead of repeating the side effect (for\n`createEmailChallenge`, re-sending the email).\n"
4309
- },
4310
- "Cursor": {
4311
- "name": "cursor",
4312
- "in": "query",
4313
- "schema": { "type": "string" },
4314
- "description": "Pagination cursor from a previous response's `meta.cursor` field.\nFormat: `{ISO-datetime}|{id}`\n"
4315
- },
4316
- "Limit": {
4283
+ "description": "cursor for contacts."
4284
+ }, {
4317
4285
  "name": "limit",
4318
4286
  "in": "query",
4319
4287
  "schema": {
@@ -4322,126 +4290,1834 @@ const openapiDocument = {
4322
4290
  "maximum": 100,
4323
4291
  "default": 50
4324
4292
  },
4325
- "description": "Number of results per page"
4326
- },
4327
- "MemoryKeyQuery": {
4328
- "name": "key",
4329
- "in": "query",
4330
- "required": true,
4331
- "description": "Memory key. Must be at most 512 UTF-8 bytes.",
4332
- "schema": {
4333
- "type": "string",
4334
- "minLength": 1,
4335
- "maxLength": 512
4336
- }
4337
- },
4338
- "MemoryScopeQueryType": {
4339
- "name": "scope_type",
4340
- "in": "query",
4341
- "required": false,
4342
- "description": "Explicit scope type. Omit to use automatic scope resolution. Pass\n`function` with `scope_id=<function-id>`, or `org` with no `scope_id`.\n",
4343
- "schema": {
4344
- "type": "string",
4345
- "enum": ["org", "function"]
4346
- }
4347
- },
4348
- "MemoryScopeId": {
4349
- "name": "scope_id",
4350
- "in": "query",
4351
4293
  "required": false,
4352
- "description": "Function id UUID when `scope_type=function`. Not valid with\n`scope_type=org`.\n",
4353
- "schema": {
4354
- "type": "string",
4355
- "format": "uuid"
4356
- }
4357
- }
4358
- },
4359
- "responses": {
4360
- "AttachmentPart": {
4361
- "description": "Original attachment bytes; never a JSON envelope or a base64 string",
4362
- "content": { "application/octet-stream": { "schema": {
4363
- "type": "string",
4364
- "format": "binary"
4365
- } } },
4366
- "headers": {
4367
- "X-Content-SHA256": {
4368
- "description": "SHA-256 hex digest of the original bytes",
4369
- "schema": {
4370
- "type": "string",
4371
- "pattern": "^[a-fA-F0-9]{64}$"
4294
+ "description": "limit for contacts."
4295
+ }],
4296
+ "responses": {
4297
+ "200": {
4298
+ "description": "Success",
4299
+ "content": { "application/json": { "schema": { "allOf": [{
4300
+ "type": "object",
4301
+ "properties": { "success": {
4302
+ "type": "boolean",
4303
+ "const": true
4304
+ } },
4305
+ "required": [
4306
+ "success",
4307
+ "data",
4308
+ "meta"
4309
+ ]
4310
+ }, {
4311
+ "type": "object",
4312
+ "properties": {
4313
+ "data": {
4314
+ "type": "array",
4315
+ "items": {
4316
+ "type": "object",
4317
+ "additionalProperties": false,
4318
+ "properties": {
4319
+ "address": {
4320
+ "type": "string",
4321
+ "format": "email",
4322
+ "maxLength": 254,
4323
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
4324
+ },
4325
+ "display_name": {
4326
+ "type": ["string", "null"],
4327
+ "maxLength": 200
4328
+ },
4329
+ "version": {
4330
+ "type": "string",
4331
+ "format": "uuid",
4332
+ "description": "Opaque CAS token. Changes on mutations and cannot be reused after deletion/recreation."
4333
+ },
4334
+ "created_at": {
4335
+ "type": "string",
4336
+ "format": "date-time"
4337
+ },
4338
+ "updated_at": {
4339
+ "type": "string",
4340
+ "format": "date-time"
4341
+ }
4342
+ },
4343
+ "required": [
4344
+ "address",
4345
+ "display_name",
4346
+ "version",
4347
+ "created_at",
4348
+ "updated_at"
4349
+ ]
4350
+ }
4351
+ },
4352
+ "meta": {
4353
+ "type": "object",
4354
+ "required": ["cursor"],
4355
+ "properties": {
4356
+ "limit": { "type": "integer" },
4357
+ "cursor": { "type": ["string", "null"] }
4358
+ }
4359
+ }
4372
4360
  }
4373
- },
4374
- "Content-Disposition": {
4375
- "description": "Safe attachment disposition with a sanitized filename",
4376
- "schema": { "type": "string" }
4377
- },
4378
- "Cache-Control": {
4379
- "description": "Attachment responses are private and must not be stored by caches",
4380
- "schema": {
4381
- "type": "string",
4382
- "example": "private, no-store"
4361
+ }] } } },
4362
+ "headers": {
4363
+ "ratelimit-limit": {
4364
+ "description": "Maximum number of requests allowed in the current window.",
4365
+ "schema": {
4366
+ "type": "integer",
4367
+ "minimum": 1,
4368
+ "example": 120
4369
+ }
4370
+ },
4371
+ "ratelimit-remaining": {
4372
+ "description": "Remaining requests in the current window.",
4373
+ "schema": {
4374
+ "type": "integer",
4375
+ "minimum": 0,
4376
+ "example": 118
4377
+ }
4378
+ },
4379
+ "ratelimit-reset": {
4380
+ "description": "Unix timestamp (seconds) when the current window resets.",
4381
+ "schema": {
4382
+ "type": "integer",
4383
+ "example": 1700000060
4384
+ }
4385
+ },
4386
+ "ratelimit-policy": {
4387
+ "description": "Rate-limit policy in `limit;w=seconds` format, e.g. `120;w=60`.",
4388
+ "schema": {
4389
+ "type": "string",
4390
+ "example": "120;w=60"
4391
+ }
4392
+ },
4393
+ "Cache-Control": {
4394
+ "schema": {
4395
+ "type": "string",
4396
+ "const": "no-store"
4397
+ },
4398
+ "description": "Always fresh policy metadata."
4383
4399
  }
4384
4400
  }
4385
- }
4386
- },
4387
- "PullContentGone": {
4388
- "description": "Queued content is no longer available",
4389
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
4390
- },
4391
- "RequestCanceled": {
4392
- "description": "Request was canceled",
4393
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
4394
- },
4395
- "PullUnavailable": {
4396
- "description": "Local receiving is unavailable",
4397
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
4398
- },
4399
- "Unauthorized": {
4400
- "description": "Invalid or missing API key",
4401
- "content": { "application/json": {
4402
- "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4403
- "example": {
4404
- "success": false,
4405
- "error": {
4406
- "code": "unauthorized",
4407
- "message": "Invalid or missing API key"
4401
+ },
4402
+ "400": {
4403
+ "description": "Invalid request parameters",
4404
+ "content": { "application/json": {
4405
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4406
+ "example": {
4407
+ "success": false,
4408
+ "error": {
4409
+ "code": "validation_error",
4410
+ "message": "Invalid domain format"
4411
+ }
4408
4412
  }
4409
- }
4410
- } }
4411
- },
4412
- "Forbidden": {
4413
- "description": "Authenticated caller lacks permission for the operation",
4414
- "content": { "application/json": {
4415
- "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4416
- "example": {
4417
- "success": false,
4418
- "error": {
4419
- "code": "forbidden",
4420
- "message": "Insufficient permissions"
4413
+ } }
4414
+ },
4415
+ "401": {
4416
+ "description": "Invalid or missing API key",
4417
+ "content": { "application/json": {
4418
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4419
+ "example": {
4420
+ "success": false,
4421
+ "error": {
4422
+ "code": "unauthorized",
4423
+ "message": "Invalid or missing API key"
4424
+ }
4421
4425
  }
4422
- }
4423
- } }
4424
- },
4425
- "NotFound": {
4426
- "description": "Resource not found",
4427
- "content": { "application/json": {
4428
- "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4429
- "example": {
4430
- "success": false,
4431
- "error": {
4432
- "code": "not_found",
4433
- "message": "Resource not found"
4426
+ } }
4427
+ },
4428
+ "403": {
4429
+ "description": "Authenticated caller lacks permission for the operation",
4430
+ "content": { "application/json": {
4431
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4432
+ "example": {
4433
+ "success": false,
4434
+ "error": {
4435
+ "code": "forbidden",
4436
+ "message": "Insufficient permissions"
4437
+ }
4434
4438
  }
4435
- }
4436
- } }
4437
- },
4438
- "ValidationError": {
4439
- "description": "Invalid request parameters",
4440
- "content": { "application/json": {
4441
- "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4442
- "example": {
4443
- "success": false,
4444
- "error": {
4439
+ } }
4440
+ },
4441
+ "404": {
4442
+ "description": "Resource not found",
4443
+ "content": { "application/json": {
4444
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4445
+ "example": {
4446
+ "success": false,
4447
+ "error": {
4448
+ "code": "not_found",
4449
+ "message": "Resource not found"
4450
+ }
4451
+ }
4452
+ } }
4453
+ },
4454
+ "409": {
4455
+ "description": "contact_conflict: write precondition did not match.",
4456
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
4457
+ },
4458
+ "429": {
4459
+ "description": "Rate limit exceeded",
4460
+ "headers": { "Retry-After": {
4461
+ "schema": { "type": "integer" },
4462
+ "description": "Seconds to wait before retrying"
4463
+ } },
4464
+ "content": { "application/json": {
4465
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4466
+ "example": {
4467
+ "success": false,
4468
+ "error": {
4469
+ "code": "rate_limit_exceeded",
4470
+ "message": "Rate limit exceeded"
4471
+ }
4472
+ }
4473
+ } }
4474
+ }
4475
+ }
4476
+ } },
4477
+ "/contacts/{address}": {
4478
+ "get": {
4479
+ "operationId": "getContact",
4480
+ "summary": "get Contact",
4481
+ "tags": ["Contacts"],
4482
+ "security": [{ "BearerAuth": [] }],
4483
+ "description": "Organization directory and agent preferences; no profiles, message history or runtime presence. Existing organization credentials use tenant permissions. Connected keys may read the directory, create address-only missing contacts using if_absent:true, and access only their own agent memberships; they cannot edit shared labels or delete directory entries. Function credentials are not granted access. Memberships require an existing local agent connection (including revoked connections) and an existing contact. PUT requires exactly one precondition. if_absent returns an existing row unchanged when supplied fields agree; otherwise 409. notify defaults false. Off-to-on generates a new server timestamp and generation; on-to-on and purpose-only edits preserve them. Disable clears activation metadata. Deletion atomically removes membership eligibility. Receivers must recheck generation/preferences before dispatch and pause admission when their policy cache is older than 30 seconds; explicit reply waits are independent. DELETE requires if_version, returns deleted:false for an absent row, and rejects stale versions of recreated rows. Pages use ascending canonical address, with meta.cursor null on the last page. Lists are live pages, not snapshots.",
4484
+ "parameters": [{
4485
+ "name": "address",
4486
+ "in": "path",
4487
+ "schema": {
4488
+ "type": "string",
4489
+ "format": "email",
4490
+ "maxLength": 254,
4491
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
4492
+ },
4493
+ "required": true,
4494
+ "description": "address for contacts."
4495
+ }],
4496
+ "responses": {
4497
+ "200": {
4498
+ "description": "Success",
4499
+ "content": { "application/json": { "schema": { "allOf": [{
4500
+ "type": "object",
4501
+ "properties": { "success": {
4502
+ "type": "boolean",
4503
+ "const": true
4504
+ } },
4505
+ "required": ["success", "data"]
4506
+ }, {
4507
+ "type": "object",
4508
+ "properties": { "data": {
4509
+ "type": "object",
4510
+ "additionalProperties": false,
4511
+ "properties": {
4512
+ "address": {
4513
+ "type": "string",
4514
+ "format": "email",
4515
+ "maxLength": 254,
4516
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
4517
+ },
4518
+ "display_name": {
4519
+ "type": ["string", "null"],
4520
+ "maxLength": 200
4521
+ },
4522
+ "version": {
4523
+ "type": "string",
4524
+ "format": "uuid",
4525
+ "description": "Opaque CAS token. Changes on mutations and cannot be reused after deletion/recreation."
4526
+ },
4527
+ "created_at": {
4528
+ "type": "string",
4529
+ "format": "date-time"
4530
+ },
4531
+ "updated_at": {
4532
+ "type": "string",
4533
+ "format": "date-time"
4534
+ }
4535
+ },
4536
+ "required": [
4537
+ "address",
4538
+ "display_name",
4539
+ "version",
4540
+ "created_at",
4541
+ "updated_at"
4542
+ ]
4543
+ } }
4544
+ }] } } },
4545
+ "headers": {
4546
+ "ratelimit-limit": {
4547
+ "description": "Maximum number of requests allowed in the current window.",
4548
+ "schema": {
4549
+ "type": "integer",
4550
+ "minimum": 1,
4551
+ "example": 120
4552
+ }
4553
+ },
4554
+ "ratelimit-remaining": {
4555
+ "description": "Remaining requests in the current window.",
4556
+ "schema": {
4557
+ "type": "integer",
4558
+ "minimum": 0,
4559
+ "example": 118
4560
+ }
4561
+ },
4562
+ "ratelimit-reset": {
4563
+ "description": "Unix timestamp (seconds) when the current window resets.",
4564
+ "schema": {
4565
+ "type": "integer",
4566
+ "example": 1700000060
4567
+ }
4568
+ },
4569
+ "ratelimit-policy": {
4570
+ "description": "Rate-limit policy in `limit;w=seconds` format, e.g. `120;w=60`.",
4571
+ "schema": {
4572
+ "type": "string",
4573
+ "example": "120;w=60"
4574
+ }
4575
+ },
4576
+ "Cache-Control": {
4577
+ "schema": {
4578
+ "type": "string",
4579
+ "const": "no-store"
4580
+ },
4581
+ "description": "Always fresh policy metadata."
4582
+ }
4583
+ }
4584
+ },
4585
+ "400": {
4586
+ "description": "Invalid request parameters",
4587
+ "content": { "application/json": {
4588
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4589
+ "example": {
4590
+ "success": false,
4591
+ "error": {
4592
+ "code": "validation_error",
4593
+ "message": "Invalid domain format"
4594
+ }
4595
+ }
4596
+ } }
4597
+ },
4598
+ "401": {
4599
+ "description": "Invalid or missing API key",
4600
+ "content": { "application/json": {
4601
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4602
+ "example": {
4603
+ "success": false,
4604
+ "error": {
4605
+ "code": "unauthorized",
4606
+ "message": "Invalid or missing API key"
4607
+ }
4608
+ }
4609
+ } }
4610
+ },
4611
+ "403": {
4612
+ "description": "Authenticated caller lacks permission for the operation",
4613
+ "content": { "application/json": {
4614
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4615
+ "example": {
4616
+ "success": false,
4617
+ "error": {
4618
+ "code": "forbidden",
4619
+ "message": "Insufficient permissions"
4620
+ }
4621
+ }
4622
+ } }
4623
+ },
4624
+ "404": {
4625
+ "description": "Resource not found",
4626
+ "content": { "application/json": {
4627
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4628
+ "example": {
4629
+ "success": false,
4630
+ "error": {
4631
+ "code": "not_found",
4632
+ "message": "Resource not found"
4633
+ }
4634
+ }
4635
+ } }
4636
+ },
4637
+ "409": {
4638
+ "description": "contact_conflict: write precondition did not match.",
4639
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
4640
+ },
4641
+ "429": {
4642
+ "description": "Rate limit exceeded",
4643
+ "headers": { "Retry-After": {
4644
+ "schema": { "type": "integer" },
4645
+ "description": "Seconds to wait before retrying"
4646
+ } },
4647
+ "content": { "application/json": {
4648
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4649
+ "example": {
4650
+ "success": false,
4651
+ "error": {
4652
+ "code": "rate_limit_exceeded",
4653
+ "message": "Rate limit exceeded"
4654
+ }
4655
+ }
4656
+ } }
4657
+ }
4658
+ }
4659
+ },
4660
+ "put": {
4661
+ "operationId": "putContact",
4662
+ "summary": "put Contact",
4663
+ "tags": ["Contacts"],
4664
+ "security": [{ "BearerAuth": [] }],
4665
+ "description": "Organization directory and agent preferences; no profiles, message history or runtime presence. Existing organization credentials use tenant permissions. Connected keys may read the directory, create address-only missing contacts using if_absent:true, and access only their own agent memberships; they cannot edit shared labels or delete directory entries. Function credentials are not granted access. Memberships require an existing local agent connection (including revoked connections) and an existing contact. PUT requires exactly one precondition. if_absent returns an existing row unchanged when supplied fields agree; otherwise 409. notify defaults false. Off-to-on generates a new server timestamp and generation; on-to-on and purpose-only edits preserve them. Disable clears activation metadata. Deletion atomically removes membership eligibility. Receivers must recheck generation/preferences before dispatch and pause admission when their policy cache is older than 30 seconds; explicit reply waits are independent. DELETE requires if_version, returns deleted:false for an absent row, and rejects stale versions of recreated rows. Pages use ascending canonical address, with meta.cursor null on the last page. Lists are live pages, not snapshots.",
4666
+ "parameters": [{
4667
+ "name": "address",
4668
+ "in": "path",
4669
+ "schema": {
4670
+ "type": "string",
4671
+ "format": "email",
4672
+ "maxLength": 254,
4673
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
4674
+ },
4675
+ "required": true,
4676
+ "description": "address for contacts."
4677
+ }],
4678
+ "requestBody": {
4679
+ "required": true,
4680
+ "content": { "application/json": { "schema": { "oneOf": [{
4681
+ "type": "object",
4682
+ "additionalProperties": false,
4683
+ "properties": {
4684
+ "display_name": {
4685
+ "type": ["string", "null"],
4686
+ "maxLength": 200
4687
+ },
4688
+ "if_absent": {
4689
+ "type": "boolean",
4690
+ "const": true
4691
+ }
4692
+ },
4693
+ "required": ["if_absent"]
4694
+ }, {
4695
+ "type": "object",
4696
+ "additionalProperties": false,
4697
+ "properties": {
4698
+ "display_name": {
4699
+ "type": ["string", "null"],
4700
+ "maxLength": 200
4701
+ },
4702
+ "if_version": {
4703
+ "type": "string",
4704
+ "format": "uuid",
4705
+ "description": "Opaque CAS token. Changes on mutations and cannot be reused after deletion/recreation."
4706
+ }
4707
+ },
4708
+ "required": ["if_version"]
4709
+ }] } } }
4710
+ },
4711
+ "responses": {
4712
+ "200": {
4713
+ "description": "Success",
4714
+ "content": { "application/json": { "schema": { "allOf": [{
4715
+ "type": "object",
4716
+ "properties": { "success": {
4717
+ "type": "boolean",
4718
+ "const": true
4719
+ } },
4720
+ "required": ["success", "data"]
4721
+ }, {
4722
+ "type": "object",
4723
+ "properties": { "data": {
4724
+ "type": "object",
4725
+ "additionalProperties": false,
4726
+ "properties": {
4727
+ "address": {
4728
+ "type": "string",
4729
+ "format": "email",
4730
+ "maxLength": 254,
4731
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
4732
+ },
4733
+ "display_name": {
4734
+ "type": ["string", "null"],
4735
+ "maxLength": 200
4736
+ },
4737
+ "version": {
4738
+ "type": "string",
4739
+ "format": "uuid",
4740
+ "description": "Opaque CAS token. Changes on mutations and cannot be reused after deletion/recreation."
4741
+ },
4742
+ "created_at": {
4743
+ "type": "string",
4744
+ "format": "date-time"
4745
+ },
4746
+ "updated_at": {
4747
+ "type": "string",
4748
+ "format": "date-time"
4749
+ }
4750
+ },
4751
+ "required": [
4752
+ "address",
4753
+ "display_name",
4754
+ "version",
4755
+ "created_at",
4756
+ "updated_at"
4757
+ ]
4758
+ } }
4759
+ }] } } },
4760
+ "headers": {
4761
+ "ratelimit-limit": {
4762
+ "description": "Maximum number of requests allowed in the current window.",
4763
+ "schema": {
4764
+ "type": "integer",
4765
+ "minimum": 1,
4766
+ "example": 120
4767
+ }
4768
+ },
4769
+ "ratelimit-remaining": {
4770
+ "description": "Remaining requests in the current window.",
4771
+ "schema": {
4772
+ "type": "integer",
4773
+ "minimum": 0,
4774
+ "example": 118
4775
+ }
4776
+ },
4777
+ "ratelimit-reset": {
4778
+ "description": "Unix timestamp (seconds) when the current window resets.",
4779
+ "schema": {
4780
+ "type": "integer",
4781
+ "example": 1700000060
4782
+ }
4783
+ },
4784
+ "ratelimit-policy": {
4785
+ "description": "Rate-limit policy in `limit;w=seconds` format, e.g. `120;w=60`.",
4786
+ "schema": {
4787
+ "type": "string",
4788
+ "example": "120;w=60"
4789
+ }
4790
+ },
4791
+ "Cache-Control": {
4792
+ "schema": {
4793
+ "type": "string",
4794
+ "const": "no-store"
4795
+ },
4796
+ "description": "Always fresh policy metadata."
4797
+ }
4798
+ }
4799
+ },
4800
+ "400": {
4801
+ "description": "Invalid request parameters",
4802
+ "content": { "application/json": {
4803
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4804
+ "example": {
4805
+ "success": false,
4806
+ "error": {
4807
+ "code": "validation_error",
4808
+ "message": "Invalid domain format"
4809
+ }
4810
+ }
4811
+ } }
4812
+ },
4813
+ "401": {
4814
+ "description": "Invalid or missing API key",
4815
+ "content": { "application/json": {
4816
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4817
+ "example": {
4818
+ "success": false,
4819
+ "error": {
4820
+ "code": "unauthorized",
4821
+ "message": "Invalid or missing API key"
4822
+ }
4823
+ }
4824
+ } }
4825
+ },
4826
+ "403": {
4827
+ "description": "Authenticated caller lacks permission for the operation",
4828
+ "content": { "application/json": {
4829
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4830
+ "example": {
4831
+ "success": false,
4832
+ "error": {
4833
+ "code": "forbidden",
4834
+ "message": "Insufficient permissions"
4835
+ }
4836
+ }
4837
+ } }
4838
+ },
4839
+ "404": {
4840
+ "description": "Resource not found",
4841
+ "content": { "application/json": {
4842
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4843
+ "example": {
4844
+ "success": false,
4845
+ "error": {
4846
+ "code": "not_found",
4847
+ "message": "Resource not found"
4848
+ }
4849
+ }
4850
+ } }
4851
+ },
4852
+ "409": {
4853
+ "description": "contact_conflict: write precondition did not match.",
4854
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
4855
+ },
4856
+ "429": {
4857
+ "description": "Rate limit exceeded",
4858
+ "headers": { "Retry-After": {
4859
+ "schema": { "type": "integer" },
4860
+ "description": "Seconds to wait before retrying"
4861
+ } },
4862
+ "content": { "application/json": {
4863
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4864
+ "example": {
4865
+ "success": false,
4866
+ "error": {
4867
+ "code": "rate_limit_exceeded",
4868
+ "message": "Rate limit exceeded"
4869
+ }
4870
+ }
4871
+ } }
4872
+ }
4873
+ }
4874
+ },
4875
+ "delete": {
4876
+ "operationId": "deleteContact",
4877
+ "summary": "delete Contact",
4878
+ "tags": ["Contacts"],
4879
+ "security": [{ "BearerAuth": [] }],
4880
+ "description": "Organization directory and agent preferences; no profiles, message history or runtime presence. Existing organization credentials use tenant permissions. Connected keys may read the directory, create address-only missing contacts using if_absent:true, and access only their own agent memberships; they cannot edit shared labels or delete directory entries. Function credentials are not granted access. Memberships require an existing local agent connection (including revoked connections) and an existing contact. PUT requires exactly one precondition. if_absent returns an existing row unchanged when supplied fields agree; otherwise 409. notify defaults false. Off-to-on generates a new server timestamp and generation; on-to-on and purpose-only edits preserve them. Disable clears activation metadata. Deletion atomically removes membership eligibility. Receivers must recheck generation/preferences before dispatch and pause admission when their policy cache is older than 30 seconds; explicit reply waits are independent. DELETE requires if_version, returns deleted:false for an absent row, and rejects stale versions of recreated rows. Pages use ascending canonical address, with meta.cursor null on the last page. Lists are live pages, not snapshots.",
4881
+ "parameters": [{
4882
+ "name": "address",
4883
+ "in": "path",
4884
+ "schema": {
4885
+ "type": "string",
4886
+ "format": "email",
4887
+ "maxLength": 254,
4888
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
4889
+ },
4890
+ "required": true,
4891
+ "description": "address for contacts."
4892
+ }, {
4893
+ "name": "if_version",
4894
+ "in": "query",
4895
+ "schema": {
4896
+ "type": "string",
4897
+ "format": "uuid",
4898
+ "description": "Opaque CAS token. Changes on mutations and cannot be reused after deletion/recreation."
4899
+ },
4900
+ "required": true,
4901
+ "description": "if_version for contacts."
4902
+ }],
4903
+ "responses": {
4904
+ "200": {
4905
+ "description": "Success",
4906
+ "content": { "application/json": { "schema": { "allOf": [{
4907
+ "type": "object",
4908
+ "properties": { "success": {
4909
+ "type": "boolean",
4910
+ "const": true
4911
+ } },
4912
+ "required": ["success", "data"]
4913
+ }, {
4914
+ "type": "object",
4915
+ "properties": { "data": {
4916
+ "type": "object",
4917
+ "additionalProperties": false,
4918
+ "properties": { "deleted": { "type": "boolean" } },
4919
+ "required": ["deleted"]
4920
+ } }
4921
+ }] } } },
4922
+ "headers": {
4923
+ "ratelimit-limit": {
4924
+ "description": "Maximum number of requests allowed in the current window.",
4925
+ "schema": {
4926
+ "type": "integer",
4927
+ "minimum": 1,
4928
+ "example": 120
4929
+ }
4930
+ },
4931
+ "ratelimit-remaining": {
4932
+ "description": "Remaining requests in the current window.",
4933
+ "schema": {
4934
+ "type": "integer",
4935
+ "minimum": 0,
4936
+ "example": 118
4937
+ }
4938
+ },
4939
+ "ratelimit-reset": {
4940
+ "description": "Unix timestamp (seconds) when the current window resets.",
4941
+ "schema": {
4942
+ "type": "integer",
4943
+ "example": 1700000060
4944
+ }
4945
+ },
4946
+ "ratelimit-policy": {
4947
+ "description": "Rate-limit policy in `limit;w=seconds` format, e.g. `120;w=60`.",
4948
+ "schema": {
4949
+ "type": "string",
4950
+ "example": "120;w=60"
4951
+ }
4952
+ },
4953
+ "Cache-Control": {
4954
+ "schema": {
4955
+ "type": "string",
4956
+ "const": "no-store"
4957
+ },
4958
+ "description": "Always fresh policy metadata."
4959
+ }
4960
+ }
4961
+ },
4962
+ "400": {
4963
+ "description": "Invalid request parameters",
4964
+ "content": { "application/json": {
4965
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4966
+ "example": {
4967
+ "success": false,
4968
+ "error": {
4969
+ "code": "validation_error",
4970
+ "message": "Invalid domain format"
4971
+ }
4972
+ }
4973
+ } }
4974
+ },
4975
+ "401": {
4976
+ "description": "Invalid or missing API key",
4977
+ "content": { "application/json": {
4978
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4979
+ "example": {
4980
+ "success": false,
4981
+ "error": {
4982
+ "code": "unauthorized",
4983
+ "message": "Invalid or missing API key"
4984
+ }
4985
+ }
4986
+ } }
4987
+ },
4988
+ "403": {
4989
+ "description": "Authenticated caller lacks permission for the operation",
4990
+ "content": { "application/json": {
4991
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
4992
+ "example": {
4993
+ "success": false,
4994
+ "error": {
4995
+ "code": "forbidden",
4996
+ "message": "Insufficient permissions"
4997
+ }
4998
+ }
4999
+ } }
5000
+ },
5001
+ "404": {
5002
+ "description": "Resource not found",
5003
+ "content": { "application/json": {
5004
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5005
+ "example": {
5006
+ "success": false,
5007
+ "error": {
5008
+ "code": "not_found",
5009
+ "message": "Resource not found"
5010
+ }
5011
+ }
5012
+ } }
5013
+ },
5014
+ "409": {
5015
+ "description": "contact_conflict: write precondition did not match.",
5016
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
5017
+ },
5018
+ "429": {
5019
+ "description": "Rate limit exceeded",
5020
+ "headers": { "Retry-After": {
5021
+ "schema": { "type": "integer" },
5022
+ "description": "Seconds to wait before retrying"
5023
+ } },
5024
+ "content": { "application/json": {
5025
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5026
+ "example": {
5027
+ "success": false,
5028
+ "error": {
5029
+ "code": "rate_limit_exceeded",
5030
+ "message": "Rate limit exceeded"
5031
+ }
5032
+ }
5033
+ } }
5034
+ }
5035
+ }
5036
+ }
5037
+ },
5038
+ "/agent-contacts/{agent_address}": { "get": {
5039
+ "operationId": "listAgentContacts",
5040
+ "summary": "list Agent Contacts",
5041
+ "tags": ["Contacts"],
5042
+ "security": [{ "BearerAuth": [] }],
5043
+ "description": "Organization directory and agent preferences; no profiles, message history or runtime presence. Existing organization credentials use tenant permissions. Connected keys may read the directory, create address-only missing contacts using if_absent:true, and access only their own agent memberships; they cannot edit shared labels or delete directory entries. Function credentials are not granted access. Memberships require an existing local agent connection (including revoked connections) and an existing contact. PUT requires exactly one precondition. if_absent returns an existing row unchanged when supplied fields agree; otherwise 409. notify defaults false. Off-to-on generates a new server timestamp and generation; on-to-on and purpose-only edits preserve them. Disable clears activation metadata. Deletion atomically removes membership eligibility. Receivers must recheck generation/preferences before dispatch and pause admission when their policy cache is older than 30 seconds; explicit reply waits are independent. DELETE requires if_version, returns deleted:false for an absent row, and rejects stale versions of recreated rows. Pages use ascending canonical address, with meta.cursor null on the last page. Lists are live pages, not snapshots.",
5044
+ "parameters": [
5045
+ {
5046
+ "name": "agent_address",
5047
+ "in": "path",
5048
+ "schema": {
5049
+ "type": "string",
5050
+ "format": "email",
5051
+ "maxLength": 254,
5052
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
5053
+ },
5054
+ "required": true,
5055
+ "description": "agent_address for contacts."
5056
+ },
5057
+ {
5058
+ "name": "cursor",
5059
+ "in": "query",
5060
+ "schema": {
5061
+ "type": "string",
5062
+ "format": "email",
5063
+ "maxLength": 254,
5064
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
5065
+ },
5066
+ "required": false,
5067
+ "description": "cursor for contacts."
5068
+ },
5069
+ {
5070
+ "name": "limit",
5071
+ "in": "query",
5072
+ "schema": {
5073
+ "type": "integer",
5074
+ "minimum": 1,
5075
+ "maximum": 100,
5076
+ "default": 50
5077
+ },
5078
+ "required": false,
5079
+ "description": "limit for contacts."
5080
+ }
5081
+ ],
5082
+ "responses": {
5083
+ "200": {
5084
+ "description": "Success",
5085
+ "content": { "application/json": { "schema": { "allOf": [{
5086
+ "type": "object",
5087
+ "properties": { "success": {
5088
+ "type": "boolean",
5089
+ "const": true
5090
+ } },
5091
+ "required": [
5092
+ "success",
5093
+ "data",
5094
+ "meta"
5095
+ ]
5096
+ }, {
5097
+ "type": "object",
5098
+ "properties": {
5099
+ "data": {
5100
+ "type": "array",
5101
+ "items": {
5102
+ "type": "object",
5103
+ "additionalProperties": false,
5104
+ "properties": {
5105
+ "agent_address": {
5106
+ "type": "string",
5107
+ "format": "email",
5108
+ "maxLength": 254,
5109
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
5110
+ },
5111
+ "contact_address": {
5112
+ "type": "string",
5113
+ "format": "email",
5114
+ "maxLength": 254,
5115
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
5116
+ },
5117
+ "purpose": {
5118
+ "type": ["string", "null"],
5119
+ "maxLength": 2e3
5120
+ },
5121
+ "notify": { "type": "boolean" },
5122
+ "notify_since": {
5123
+ "type": ["string", "null"],
5124
+ "format": "date-time"
5125
+ },
5126
+ "notification_generation": {
5127
+ "type": ["string", "null"],
5128
+ "format": "uuid"
5129
+ },
5130
+ "version": {
5131
+ "type": "string",
5132
+ "format": "uuid",
5133
+ "description": "Opaque CAS token. Changes on mutations and cannot be reused after deletion/recreation."
5134
+ },
5135
+ "created_at": {
5136
+ "type": "string",
5137
+ "format": "date-time"
5138
+ },
5139
+ "updated_at": {
5140
+ "type": "string",
5141
+ "format": "date-time"
5142
+ }
5143
+ },
5144
+ "required": [
5145
+ "agent_address",
5146
+ "contact_address",
5147
+ "purpose",
5148
+ "notify",
5149
+ "notify_since",
5150
+ "notification_generation",
5151
+ "version",
5152
+ "created_at",
5153
+ "updated_at"
5154
+ ]
5155
+ }
5156
+ },
5157
+ "meta": {
5158
+ "type": "object",
5159
+ "required": ["cursor"],
5160
+ "properties": {
5161
+ "limit": { "type": "integer" },
5162
+ "cursor": { "type": ["string", "null"] }
5163
+ }
5164
+ }
5165
+ }
5166
+ }] } } },
5167
+ "headers": {
5168
+ "ratelimit-limit": {
5169
+ "description": "Maximum number of requests allowed in the current window.",
5170
+ "schema": {
5171
+ "type": "integer",
5172
+ "minimum": 1,
5173
+ "example": 120
5174
+ }
5175
+ },
5176
+ "ratelimit-remaining": {
5177
+ "description": "Remaining requests in the current window.",
5178
+ "schema": {
5179
+ "type": "integer",
5180
+ "minimum": 0,
5181
+ "example": 118
5182
+ }
5183
+ },
5184
+ "ratelimit-reset": {
5185
+ "description": "Unix timestamp (seconds) when the current window resets.",
5186
+ "schema": {
5187
+ "type": "integer",
5188
+ "example": 1700000060
5189
+ }
5190
+ },
5191
+ "ratelimit-policy": {
5192
+ "description": "Rate-limit policy in `limit;w=seconds` format, e.g. `120;w=60`.",
5193
+ "schema": {
5194
+ "type": "string",
5195
+ "example": "120;w=60"
5196
+ }
5197
+ },
5198
+ "Cache-Control": {
5199
+ "schema": {
5200
+ "type": "string",
5201
+ "const": "no-store"
5202
+ },
5203
+ "description": "Always fresh policy metadata."
5204
+ }
5205
+ }
5206
+ },
5207
+ "400": {
5208
+ "description": "Invalid request parameters",
5209
+ "content": { "application/json": {
5210
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5211
+ "example": {
5212
+ "success": false,
5213
+ "error": {
5214
+ "code": "validation_error",
5215
+ "message": "Invalid domain format"
5216
+ }
5217
+ }
5218
+ } }
5219
+ },
5220
+ "401": {
5221
+ "description": "Invalid or missing API key",
5222
+ "content": { "application/json": {
5223
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5224
+ "example": {
5225
+ "success": false,
5226
+ "error": {
5227
+ "code": "unauthorized",
5228
+ "message": "Invalid or missing API key"
5229
+ }
5230
+ }
5231
+ } }
5232
+ },
5233
+ "403": {
5234
+ "description": "Authenticated caller lacks permission for the operation",
5235
+ "content": { "application/json": {
5236
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5237
+ "example": {
5238
+ "success": false,
5239
+ "error": {
5240
+ "code": "forbidden",
5241
+ "message": "Insufficient permissions"
5242
+ }
5243
+ }
5244
+ } }
5245
+ },
5246
+ "404": {
5247
+ "description": "Resource not found",
5248
+ "content": { "application/json": {
5249
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5250
+ "example": {
5251
+ "success": false,
5252
+ "error": {
5253
+ "code": "not_found",
5254
+ "message": "Resource not found"
5255
+ }
5256
+ }
5257
+ } }
5258
+ },
5259
+ "409": {
5260
+ "description": "contact_conflict: write precondition did not match.",
5261
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
5262
+ },
5263
+ "429": {
5264
+ "description": "Rate limit exceeded",
5265
+ "headers": { "Retry-After": {
5266
+ "schema": { "type": "integer" },
5267
+ "description": "Seconds to wait before retrying"
5268
+ } },
5269
+ "content": { "application/json": {
5270
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5271
+ "example": {
5272
+ "success": false,
5273
+ "error": {
5274
+ "code": "rate_limit_exceeded",
5275
+ "message": "Rate limit exceeded"
5276
+ }
5277
+ }
5278
+ } }
5279
+ }
5280
+ }
5281
+ } },
5282
+ "/agent-contacts/{agent_address}/{contact_address}": {
5283
+ "put": {
5284
+ "operationId": "putAgentContact",
5285
+ "summary": "put Agent Contact",
5286
+ "tags": ["Contacts"],
5287
+ "security": [{ "BearerAuth": [] }],
5288
+ "description": "Organization directory and agent preferences; no profiles, message history or runtime presence. Existing organization credentials use tenant permissions. Connected keys may read the directory, create address-only missing contacts using if_absent:true, and access only their own agent memberships; they cannot edit shared labels or delete directory entries. Function credentials are not granted access. Memberships require an existing local agent connection (including revoked connections) and an existing contact. PUT requires exactly one precondition. if_absent returns an existing row unchanged when supplied fields agree; otherwise 409. notify defaults false. Off-to-on generates a new server timestamp and generation; on-to-on and purpose-only edits preserve them. Disable clears activation metadata. Deletion atomically removes membership eligibility. Receivers must recheck generation/preferences before dispatch and pause admission when their policy cache is older than 30 seconds; explicit reply waits are independent. DELETE requires if_version, returns deleted:false for an absent row, and rejects stale versions of recreated rows. Pages use ascending canonical address, with meta.cursor null on the last page. Lists are live pages, not snapshots.",
5289
+ "parameters": [{
5290
+ "name": "agent_address",
5291
+ "in": "path",
5292
+ "schema": {
5293
+ "type": "string",
5294
+ "format": "email",
5295
+ "maxLength": 254,
5296
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
5297
+ },
5298
+ "required": true,
5299
+ "description": "agent_address for contacts."
5300
+ }, {
5301
+ "name": "contact_address",
5302
+ "in": "path",
5303
+ "schema": {
5304
+ "type": "string",
5305
+ "format": "email",
5306
+ "maxLength": 254,
5307
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
5308
+ },
5309
+ "required": true,
5310
+ "description": "contact_address for contacts."
5311
+ }],
5312
+ "requestBody": {
5313
+ "required": true,
5314
+ "content": { "application/json": { "schema": { "oneOf": [{
5315
+ "type": "object",
5316
+ "additionalProperties": false,
5317
+ "properties": {
5318
+ "purpose": {
5319
+ "type": ["string", "null"],
5320
+ "maxLength": 2e3
5321
+ },
5322
+ "notify": { "type": "boolean" },
5323
+ "if_absent": {
5324
+ "type": "boolean",
5325
+ "const": true
5326
+ }
5327
+ },
5328
+ "required": ["if_absent"]
5329
+ }, {
5330
+ "type": "object",
5331
+ "additionalProperties": false,
5332
+ "properties": {
5333
+ "purpose": {
5334
+ "type": ["string", "null"],
5335
+ "maxLength": 2e3
5336
+ },
5337
+ "notify": { "type": "boolean" },
5338
+ "if_version": {
5339
+ "type": "string",
5340
+ "format": "uuid",
5341
+ "description": "Opaque CAS token. Changes on mutations and cannot be reused after deletion/recreation."
5342
+ }
5343
+ },
5344
+ "required": ["if_version"]
5345
+ }] } } }
5346
+ },
5347
+ "responses": {
5348
+ "200": {
5349
+ "description": "Success",
5350
+ "content": { "application/json": { "schema": { "allOf": [{
5351
+ "type": "object",
5352
+ "properties": { "success": {
5353
+ "type": "boolean",
5354
+ "const": true
5355
+ } },
5356
+ "required": ["success", "data"]
5357
+ }, {
5358
+ "type": "object",
5359
+ "properties": { "data": {
5360
+ "type": "object",
5361
+ "additionalProperties": false,
5362
+ "properties": {
5363
+ "agent_address": {
5364
+ "type": "string",
5365
+ "format": "email",
5366
+ "maxLength": 254,
5367
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
5368
+ },
5369
+ "contact_address": {
5370
+ "type": "string",
5371
+ "format": "email",
5372
+ "maxLength": 254,
5373
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
5374
+ },
5375
+ "purpose": {
5376
+ "type": ["string", "null"],
5377
+ "maxLength": 2e3
5378
+ },
5379
+ "notify": { "type": "boolean" },
5380
+ "notify_since": {
5381
+ "type": ["string", "null"],
5382
+ "format": "date-time"
5383
+ },
5384
+ "notification_generation": {
5385
+ "type": ["string", "null"],
5386
+ "format": "uuid"
5387
+ },
5388
+ "version": {
5389
+ "type": "string",
5390
+ "format": "uuid",
5391
+ "description": "Opaque CAS token. Changes on mutations and cannot be reused after deletion/recreation."
5392
+ },
5393
+ "created_at": {
5394
+ "type": "string",
5395
+ "format": "date-time"
5396
+ },
5397
+ "updated_at": {
5398
+ "type": "string",
5399
+ "format": "date-time"
5400
+ }
5401
+ },
5402
+ "required": [
5403
+ "agent_address",
5404
+ "contact_address",
5405
+ "purpose",
5406
+ "notify",
5407
+ "notify_since",
5408
+ "notification_generation",
5409
+ "version",
5410
+ "created_at",
5411
+ "updated_at"
5412
+ ]
5413
+ } }
5414
+ }] } } },
5415
+ "headers": {
5416
+ "ratelimit-limit": {
5417
+ "description": "Maximum number of requests allowed in the current window.",
5418
+ "schema": {
5419
+ "type": "integer",
5420
+ "minimum": 1,
5421
+ "example": 120
5422
+ }
5423
+ },
5424
+ "ratelimit-remaining": {
5425
+ "description": "Remaining requests in the current window.",
5426
+ "schema": {
5427
+ "type": "integer",
5428
+ "minimum": 0,
5429
+ "example": 118
5430
+ }
5431
+ },
5432
+ "ratelimit-reset": {
5433
+ "description": "Unix timestamp (seconds) when the current window resets.",
5434
+ "schema": {
5435
+ "type": "integer",
5436
+ "example": 1700000060
5437
+ }
5438
+ },
5439
+ "ratelimit-policy": {
5440
+ "description": "Rate-limit policy in `limit;w=seconds` format, e.g. `120;w=60`.",
5441
+ "schema": {
5442
+ "type": "string",
5443
+ "example": "120;w=60"
5444
+ }
5445
+ },
5446
+ "Cache-Control": {
5447
+ "schema": {
5448
+ "type": "string",
5449
+ "const": "no-store"
5450
+ },
5451
+ "description": "Always fresh policy metadata."
5452
+ }
5453
+ }
5454
+ },
5455
+ "400": {
5456
+ "description": "Invalid request parameters",
5457
+ "content": { "application/json": {
5458
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5459
+ "example": {
5460
+ "success": false,
5461
+ "error": {
5462
+ "code": "validation_error",
5463
+ "message": "Invalid domain format"
5464
+ }
5465
+ }
5466
+ } }
5467
+ },
5468
+ "401": {
5469
+ "description": "Invalid or missing API key",
5470
+ "content": { "application/json": {
5471
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5472
+ "example": {
5473
+ "success": false,
5474
+ "error": {
5475
+ "code": "unauthorized",
5476
+ "message": "Invalid or missing API key"
5477
+ }
5478
+ }
5479
+ } }
5480
+ },
5481
+ "403": {
5482
+ "description": "Authenticated caller lacks permission for the operation",
5483
+ "content": { "application/json": {
5484
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5485
+ "example": {
5486
+ "success": false,
5487
+ "error": {
5488
+ "code": "forbidden",
5489
+ "message": "Insufficient permissions"
5490
+ }
5491
+ }
5492
+ } }
5493
+ },
5494
+ "404": {
5495
+ "description": "Resource not found",
5496
+ "content": { "application/json": {
5497
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5498
+ "example": {
5499
+ "success": false,
5500
+ "error": {
5501
+ "code": "not_found",
5502
+ "message": "Resource not found"
5503
+ }
5504
+ }
5505
+ } }
5506
+ },
5507
+ "409": {
5508
+ "description": "contact_conflict: write precondition did not match.",
5509
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
5510
+ },
5511
+ "429": {
5512
+ "description": "Rate limit exceeded",
5513
+ "headers": { "Retry-After": {
5514
+ "schema": { "type": "integer" },
5515
+ "description": "Seconds to wait before retrying"
5516
+ } },
5517
+ "content": { "application/json": {
5518
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5519
+ "example": {
5520
+ "success": false,
5521
+ "error": {
5522
+ "code": "rate_limit_exceeded",
5523
+ "message": "Rate limit exceeded"
5524
+ }
5525
+ }
5526
+ } }
5527
+ }
5528
+ }
5529
+ },
5530
+ "delete": {
5531
+ "operationId": "deleteAgentContact",
5532
+ "summary": "delete Agent Contact",
5533
+ "tags": ["Contacts"],
5534
+ "security": [{ "BearerAuth": [] }],
5535
+ "description": "Organization directory and agent preferences; no profiles, message history or runtime presence. Existing organization credentials use tenant permissions. Connected keys may read the directory, create address-only missing contacts using if_absent:true, and access only their own agent memberships; they cannot edit shared labels or delete directory entries. Function credentials are not granted access. Memberships require an existing local agent connection (including revoked connections) and an existing contact. PUT requires exactly one precondition. if_absent returns an existing row unchanged when supplied fields agree; otherwise 409. notify defaults false. Off-to-on generates a new server timestamp and generation; on-to-on and purpose-only edits preserve them. Disable clears activation metadata. Deletion atomically removes membership eligibility. Receivers must recheck generation/preferences before dispatch and pause admission when their policy cache is older than 30 seconds; explicit reply waits are independent. DELETE requires if_version, returns deleted:false for an absent row, and rejects stale versions of recreated rows. Pages use ascending canonical address, with meta.cursor null on the last page. Lists are live pages, not snapshots.",
5536
+ "parameters": [
5537
+ {
5538
+ "name": "agent_address",
5539
+ "in": "path",
5540
+ "schema": {
5541
+ "type": "string",
5542
+ "format": "email",
5543
+ "maxLength": 254,
5544
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
5545
+ },
5546
+ "required": true,
5547
+ "description": "agent_address for contacts."
5548
+ },
5549
+ {
5550
+ "name": "contact_address",
5551
+ "in": "path",
5552
+ "schema": {
5553
+ "type": "string",
5554
+ "format": "email",
5555
+ "maxLength": 254,
5556
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
5557
+ },
5558
+ "required": true,
5559
+ "description": "contact_address for contacts."
5560
+ },
5561
+ {
5562
+ "name": "if_version",
5563
+ "in": "query",
5564
+ "schema": {
5565
+ "type": "string",
5566
+ "format": "uuid",
5567
+ "description": "Opaque CAS token. Changes on mutations and cannot be reused after deletion/recreation."
5568
+ },
5569
+ "required": true,
5570
+ "description": "if_version for contacts."
5571
+ }
5572
+ ],
5573
+ "responses": {
5574
+ "200": {
5575
+ "description": "Success",
5576
+ "content": { "application/json": { "schema": { "allOf": [{
5577
+ "type": "object",
5578
+ "properties": { "success": {
5579
+ "type": "boolean",
5580
+ "const": true
5581
+ } },
5582
+ "required": ["success", "data"]
5583
+ }, {
5584
+ "type": "object",
5585
+ "properties": { "data": {
5586
+ "type": "object",
5587
+ "additionalProperties": false,
5588
+ "properties": { "deleted": { "type": "boolean" } },
5589
+ "required": ["deleted"]
5590
+ } }
5591
+ }] } } },
5592
+ "headers": {
5593
+ "ratelimit-limit": {
5594
+ "description": "Maximum number of requests allowed in the current window.",
5595
+ "schema": {
5596
+ "type": "integer",
5597
+ "minimum": 1,
5598
+ "example": 120
5599
+ }
5600
+ },
5601
+ "ratelimit-remaining": {
5602
+ "description": "Remaining requests in the current window.",
5603
+ "schema": {
5604
+ "type": "integer",
5605
+ "minimum": 0,
5606
+ "example": 118
5607
+ }
5608
+ },
5609
+ "ratelimit-reset": {
5610
+ "description": "Unix timestamp (seconds) when the current window resets.",
5611
+ "schema": {
5612
+ "type": "integer",
5613
+ "example": 1700000060
5614
+ }
5615
+ },
5616
+ "ratelimit-policy": {
5617
+ "description": "Rate-limit policy in `limit;w=seconds` format, e.g. `120;w=60`.",
5618
+ "schema": {
5619
+ "type": "string",
5620
+ "example": "120;w=60"
5621
+ }
5622
+ },
5623
+ "Cache-Control": {
5624
+ "schema": {
5625
+ "type": "string",
5626
+ "const": "no-store"
5627
+ },
5628
+ "description": "Always fresh policy metadata."
5629
+ }
5630
+ }
5631
+ },
5632
+ "400": {
5633
+ "description": "Invalid request parameters",
5634
+ "content": { "application/json": {
5635
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5636
+ "example": {
5637
+ "success": false,
5638
+ "error": {
5639
+ "code": "validation_error",
5640
+ "message": "Invalid domain format"
5641
+ }
5642
+ }
5643
+ } }
5644
+ },
5645
+ "401": {
5646
+ "description": "Invalid or missing API key",
5647
+ "content": { "application/json": {
5648
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5649
+ "example": {
5650
+ "success": false,
5651
+ "error": {
5652
+ "code": "unauthorized",
5653
+ "message": "Invalid or missing API key"
5654
+ }
5655
+ }
5656
+ } }
5657
+ },
5658
+ "403": {
5659
+ "description": "Authenticated caller lacks permission for the operation",
5660
+ "content": { "application/json": {
5661
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5662
+ "example": {
5663
+ "success": false,
5664
+ "error": {
5665
+ "code": "forbidden",
5666
+ "message": "Insufficient permissions"
5667
+ }
5668
+ }
5669
+ } }
5670
+ },
5671
+ "404": {
5672
+ "description": "Resource not found",
5673
+ "content": { "application/json": {
5674
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5675
+ "example": {
5676
+ "success": false,
5677
+ "error": {
5678
+ "code": "not_found",
5679
+ "message": "Resource not found"
5680
+ }
5681
+ }
5682
+ } }
5683
+ },
5684
+ "409": {
5685
+ "description": "contact_conflict: write precondition did not match.",
5686
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
5687
+ },
5688
+ "429": {
5689
+ "description": "Rate limit exceeded",
5690
+ "headers": { "Retry-After": {
5691
+ "schema": { "type": "integer" },
5692
+ "description": "Seconds to wait before retrying"
5693
+ } },
5694
+ "content": { "application/json": {
5695
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5696
+ "example": {
5697
+ "success": false,
5698
+ "error": {
5699
+ "code": "rate_limit_exceeded",
5700
+ "message": "Rate limit exceeded"
5701
+ }
5702
+ }
5703
+ } }
5704
+ }
5705
+ }
5706
+ }
5707
+ },
5708
+ "/agent-connections/claim": { "post": {
5709
+ "operationId": "claimAgentConnection",
5710
+ "tags": ["Agent Connections"],
5711
+ "summary": "claim Agent Connection",
5712
+ "description": "Address-bound external runtime pairing. Management operations require an organization owner or admin session or OAuth token; members and organization API keys cannot manage connections. Claim is authorized only by its one-use invitation. Connected means a real challenge was received and a reply sent by the current bound credential was received back. Invitations expire after 15 minutes. Reconnection preserves the address and revokes previous credentials. Status responses contain no credentials. Runtime credentials allow only address-scoped mail operations, organization note reads and own-address note writes. The credential is returned once. If the claim response is lost or the outcome is unknown, request a fresh owner invitation instead of retrying the consumed invitation.",
5713
+ "security": [],
5714
+ "parameters": [{
5715
+ "name": "Idempotency-Key",
5716
+ "in": "header",
5717
+ "required": false,
5718
+ "description": "This header does not enable credential replay for this one-use claim. If the response is lost or the outcome is unknown, request a fresh owner invitation. Do not assume that retrying the same key or payload can recover the returned credential.",
5719
+ "schema": {
5720
+ "type": "string",
5721
+ "minLength": 1,
5722
+ "maxLength": 255
5723
+ }
5724
+ }],
5725
+ "responses": {
5726
+ "200": {
5727
+ "description": "Success",
5728
+ "headers": { "Cache-Control": {
5729
+ "description": "Credentials and status must not be cached.",
5730
+ "schema": {
5731
+ "type": "string",
5732
+ "const": "no-store"
5733
+ }
5734
+ } },
5735
+ "content": { "application/json": { "schema": {
5736
+ "type": "object",
5737
+ "required": ["success", "data"],
5738
+ "properties": {
5739
+ "success": {
5740
+ "const": true,
5741
+ "type": "boolean"
5742
+ },
5743
+ "data": {
5744
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
5745
+ "type": "object",
5746
+ "properties": {
5747
+ "connection": {
5748
+ "type": "object",
5749
+ "properties": {
5750
+ "address": {
5751
+ "type": "string",
5752
+ "format": "email",
5753
+ "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
5754
+ },
5755
+ "name": {
5756
+ "type": "string",
5757
+ "minLength": 1,
5758
+ "maxLength": 80
5759
+ },
5760
+ "owner_address": {
5761
+ "type": "string",
5762
+ "format": "email",
5763
+ "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
5764
+ },
5765
+ "status": {
5766
+ "type": "string",
5767
+ "enum": [
5768
+ "pending",
5769
+ "claimed",
5770
+ "connected",
5771
+ "revoked"
5772
+ ]
5773
+ },
5774
+ "created_at": {
5775
+ "type": "string",
5776
+ "format": "date-time",
5777
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
5778
+ },
5779
+ "updated_at": {
5780
+ "type": "string",
5781
+ "format": "date-time",
5782
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
5783
+ },
5784
+ "claimed_at": { "anyOf": [{
5785
+ "type": "string",
5786
+ "format": "date-time",
5787
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
5788
+ }, { "type": "null" }] },
5789
+ "verified_at": { "anyOf": [{
5790
+ "type": "string",
5791
+ "format": "date-time",
5792
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
5793
+ }, { "type": "null" }] },
5794
+ "last_seen_at": { "anyOf": [{
5795
+ "type": "string",
5796
+ "format": "date-time",
5797
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
5798
+ }, { "type": "null" }] }
5799
+ },
5800
+ "required": [
5801
+ "address",
5802
+ "name",
5803
+ "owner_address",
5804
+ "status",
5805
+ "created_at",
5806
+ "updated_at",
5807
+ "claimed_at",
5808
+ "verified_at",
5809
+ "last_seen_at"
5810
+ ],
5811
+ "additionalProperties": false
5812
+ },
5813
+ "org_id": {
5814
+ "type": "string",
5815
+ "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
5816
+ },
5817
+ "owner_address": {
5818
+ "type": "string",
5819
+ "format": "email",
5820
+ "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
5821
+ },
5822
+ "api_key": { "type": "string" },
5823
+ "api_base_url": {
5824
+ "type": "string",
5825
+ "format": "uri"
5826
+ }
5827
+ },
5828
+ "required": [
5829
+ "connection",
5830
+ "org_id",
5831
+ "owner_address",
5832
+ "api_key",
5833
+ "api_base_url"
5834
+ ],
5835
+ "additionalProperties": false
5836
+ }
5837
+ }
5838
+ } } }
5839
+ },
5840
+ "400": {
5841
+ "description": "Invalid request parameters",
5842
+ "content": { "application/json": {
5843
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5844
+ "example": {
5845
+ "success": false,
5846
+ "error": {
5847
+ "code": "validation_error",
5848
+ "message": "Invalid domain format"
5849
+ }
5850
+ }
5851
+ } }
5852
+ },
5853
+ "401": {
5854
+ "description": "Invalid or missing API key",
5855
+ "content": { "application/json": {
5856
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5857
+ "example": {
5858
+ "success": false,
5859
+ "error": {
5860
+ "code": "unauthorized",
5861
+ "message": "Invalid or missing API key"
5862
+ }
5863
+ }
5864
+ } }
5865
+ },
5866
+ "403": {
5867
+ "description": "Authenticated caller lacks permission for the operation",
5868
+ "content": { "application/json": {
5869
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5870
+ "example": {
5871
+ "success": false,
5872
+ "error": {
5873
+ "code": "forbidden",
5874
+ "message": "Insufficient permissions"
5875
+ }
5876
+ }
5877
+ } }
5878
+ },
5879
+ "404": {
5880
+ "description": "Resource not found",
5881
+ "content": { "application/json": {
5882
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5883
+ "example": {
5884
+ "success": false,
5885
+ "error": {
5886
+ "code": "not_found",
5887
+ "message": "Resource not found"
5888
+ }
5889
+ }
5890
+ } }
5891
+ },
5892
+ "409": {
5893
+ "description": "The request conflicts with the current state of the resource",
5894
+ "content": { "application/json": {
5895
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5896
+ "example": {
5897
+ "success": false,
5898
+ "error": {
5899
+ "code": "conflict",
5900
+ "message": "settlement already in progress"
5901
+ }
5902
+ }
5903
+ } }
5904
+ },
5905
+ "429": {
5906
+ "description": "Rate limit exceeded",
5907
+ "headers": { "Retry-After": {
5908
+ "schema": { "type": "integer" },
5909
+ "description": "Seconds to wait before retrying"
5910
+ } },
5911
+ "content": { "application/json": {
5912
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
5913
+ "example": {
5914
+ "success": false,
5915
+ "error": {
5916
+ "code": "rate_limit_exceeded",
5917
+ "message": "Rate limit exceeded"
5918
+ }
5919
+ }
5920
+ } }
5921
+ }
5922
+ },
5923
+ "requestBody": {
5924
+ "required": true,
5925
+ "content": { "application/json": { "schema": {
5926
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
5927
+ "type": "object",
5928
+ "properties": { "token": {
5929
+ "type": "string",
5930
+ "minLength": 32,
5931
+ "maxLength": 256
5932
+ } },
5933
+ "required": ["token"],
5934
+ "additionalProperties": false
5935
+ } } }
5936
+ }
5937
+ } }
5938
+ },
5939
+ "components": {
5940
+ "securitySchemes": {
5941
+ "BearerAuth": {
5942
+ "type": "http",
5943
+ "scheme": "bearer",
5944
+ "description": "API key with `prim_` prefix: `Authorization: Bearer prim_<key>`"
5945
+ },
5946
+ "DownloadToken": {
5947
+ "type": "apiKey",
5948
+ "in": "query",
5949
+ "name": "token",
5950
+ "description": "Signed download token provided in webhook payloads"
5951
+ }
5952
+ },
5953
+ "parameters": {
5954
+ "AttachmentPartIndex": {
5955
+ "name": "part_index",
5956
+ "in": "path",
5957
+ "required": true,
5958
+ "description": "The attachment metadata `part_index`, not its offset in the attachments array",
5959
+ "schema": {
5960
+ "type": "integer",
5961
+ "format": "int32",
5962
+ "minimum": 0,
5963
+ "maximum": 2147483647
5964
+ }
5965
+ },
5966
+ "ResourceId": {
5967
+ "name": "id",
5968
+ "in": "path",
5969
+ "required": true,
5970
+ "schema": {
5971
+ "type": "string",
5972
+ "format": "uuid"
5973
+ },
5974
+ "description": "Resource UUID"
5975
+ },
5976
+ "IdempotencyKey": {
5977
+ "name": "idempotency-key",
5978
+ "in": "header",
5979
+ "required": false,
5980
+ "schema": {
5981
+ "type": "string",
5982
+ "maxLength": 255
5983
+ },
5984
+ "description": "Optional idempotency key. Retrying a request with the same key returns\nthe original result instead of repeating the side effect (for\n`createEmailChallenge`, re-sending the email).\n"
5985
+ },
5986
+ "Cursor": {
5987
+ "name": "cursor",
5988
+ "in": "query",
5989
+ "schema": { "type": "string" },
5990
+ "description": "Pagination cursor from a previous response's `meta.cursor` field.\nFormat: `{ISO-datetime}|{id}`\n"
5991
+ },
5992
+ "Limit": {
5993
+ "name": "limit",
5994
+ "in": "query",
5995
+ "schema": {
5996
+ "type": "integer",
5997
+ "minimum": 1,
5998
+ "maximum": 100,
5999
+ "default": 50
6000
+ },
6001
+ "description": "Number of results per page"
6002
+ },
6003
+ "MemoryKeyQuery": {
6004
+ "name": "key",
6005
+ "in": "query",
6006
+ "required": true,
6007
+ "description": "Memory key. Must be at most 512 UTF-8 bytes.",
6008
+ "schema": {
6009
+ "type": "string",
6010
+ "minLength": 1,
6011
+ "maxLength": 512
6012
+ }
6013
+ },
6014
+ "MemoryScopeQueryType": {
6015
+ "name": "scope_type",
6016
+ "in": "query",
6017
+ "required": false,
6018
+ "description": "Explicit scope type. Omit to use automatic scope resolution. Pass\n`function` with `scope_id=<function-id>`, or `org` with no `scope_id`.\n",
6019
+ "schema": {
6020
+ "type": "string",
6021
+ "enum": ["org", "function"]
6022
+ }
6023
+ },
6024
+ "MemoryScopeId": {
6025
+ "name": "scope_id",
6026
+ "in": "query",
6027
+ "required": false,
6028
+ "description": "Function id UUID when `scope_type=function`. Not valid with\n`scope_type=org`.\n",
6029
+ "schema": {
6030
+ "type": "string",
6031
+ "format": "uuid"
6032
+ }
6033
+ }
6034
+ },
6035
+ "responses": {
6036
+ "AttachmentPart": {
6037
+ "description": "Original attachment bytes; never a JSON envelope or a base64 string",
6038
+ "content": { "application/octet-stream": { "schema": {
6039
+ "type": "string",
6040
+ "format": "binary"
6041
+ } } },
6042
+ "headers": {
6043
+ "X-Content-SHA256": {
6044
+ "description": "SHA-256 hex digest of the original bytes",
6045
+ "schema": {
6046
+ "type": "string",
6047
+ "pattern": "^[a-fA-F0-9]{64}$"
6048
+ }
6049
+ },
6050
+ "Content-Disposition": {
6051
+ "description": "Safe attachment disposition with a sanitized filename",
6052
+ "schema": { "type": "string" }
6053
+ },
6054
+ "Cache-Control": {
6055
+ "description": "Attachment responses are private and must not be stored by caches",
6056
+ "schema": {
6057
+ "type": "string",
6058
+ "example": "private, no-store"
6059
+ }
6060
+ }
6061
+ }
6062
+ },
6063
+ "PullContentGone": {
6064
+ "description": "Queued content is no longer available",
6065
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
6066
+ },
6067
+ "RequestCanceled": {
6068
+ "description": "Request was canceled",
6069
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
6070
+ },
6071
+ "PullUnavailable": {
6072
+ "description": "Local receiving is unavailable",
6073
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
6074
+ },
6075
+ "Unauthorized": {
6076
+ "description": "Invalid or missing API key",
6077
+ "content": { "application/json": {
6078
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
6079
+ "example": {
6080
+ "success": false,
6081
+ "error": {
6082
+ "code": "unauthorized",
6083
+ "message": "Invalid or missing API key"
6084
+ }
6085
+ }
6086
+ } }
6087
+ },
6088
+ "Forbidden": {
6089
+ "description": "Authenticated caller lacks permission for the operation",
6090
+ "content": { "application/json": {
6091
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
6092
+ "example": {
6093
+ "success": false,
6094
+ "error": {
6095
+ "code": "forbidden",
6096
+ "message": "Insufficient permissions"
6097
+ }
6098
+ }
6099
+ } }
6100
+ },
6101
+ "NotFound": {
6102
+ "description": "Resource not found",
6103
+ "content": { "application/json": {
6104
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
6105
+ "example": {
6106
+ "success": false,
6107
+ "error": {
6108
+ "code": "not_found",
6109
+ "message": "Resource not found"
6110
+ }
6111
+ }
6112
+ } }
6113
+ },
6114
+ "ValidationError": {
6115
+ "description": "Invalid request parameters",
6116
+ "content": { "application/json": {
6117
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
6118
+ "example": {
6119
+ "success": false,
6120
+ "error": {
4445
6121
  "code": "validation_error",
4446
6122
  "message": "Invalid domain format"
4447
6123
  }
@@ -5472,7 +7148,15 @@ const openapiDocument = {
5472
7148
  "credit_code_not_eligible",
5473
7149
  "credit_code_balance_cap",
5474
7150
  "rate_limited",
5475
- "service_unavailable"
7151
+ "service_unavailable",
7152
+ "connection_domain_unavailable",
7153
+ "connection_address_unavailable",
7154
+ "connection_owner_address_invalid",
7155
+ "connection_invitation_unavailable",
7156
+ "agent_connection_scope_forbidden",
7157
+ "address_note_conflict",
7158
+ "address_not_controlled",
7159
+ "contact_conflict"
5476
7160
  ]
5477
7161
  },
5478
7162
  "message": { "type": "string" },
@@ -11790,15 +13474,228 @@ const operationManifest = [
11790
13474
  {
11791
13475
  "binaryResponse": false,
11792
13476
  "bodyRequired": false,
11793
- "command": "get-account",
11794
- "description": null,
13477
+ "command": "get-account",
13478
+ "description": null,
13479
+ "hasJsonBody": false,
13480
+ "method": "GET",
13481
+ "operationId": "getAccount",
13482
+ "path": "/account",
13483
+ "pathParams": [],
13484
+ "queryParams": [],
13485
+ "requestSchema": null,
13486
+ "responseSchema": {
13487
+ "type": "object",
13488
+ "properties": {
13489
+ "id": {
13490
+ "type": "string",
13491
+ "format": "uuid"
13492
+ },
13493
+ "email": { "type": "string" },
13494
+ "plan": { "type": "string" },
13495
+ "limits": {
13496
+ "type": "object",
13497
+ "description": "Plan-derived quota limits for an account.",
13498
+ "properties": {
13499
+ "storage_mb": { "type": "number" },
13500
+ "send_per_hour": { "type": "number" },
13501
+ "send_per_day": { "type": "number" },
13502
+ "api_per_minute": { "type": "number" },
13503
+ "webhooks_max_global": { "type": ["number", "null"] },
13504
+ "webhooks_per_domain": { "type": "boolean" },
13505
+ "filters_per_domain": { "type": "boolean" },
13506
+ "spam_thresholds_per_domain": { "type": "boolean" }
13507
+ },
13508
+ "required": [
13509
+ "storage_mb",
13510
+ "send_per_hour",
13511
+ "send_per_day",
13512
+ "api_per_minute",
13513
+ "webhooks_max_global",
13514
+ "webhooks_per_domain",
13515
+ "filters_per_domain",
13516
+ "spam_thresholds_per_domain"
13517
+ ]
13518
+ },
13519
+ "entitlements": {
13520
+ "type": "array",
13521
+ "items": { "type": "string" },
13522
+ "description": "Granted org entitlement keys (sorted). A headless caller reads its\ncapabilities here — e.g. an emailless agent seeing only\n[\"send_mail\", \"send_to_known_addresses\"] knows it is reply-only.\n"
13523
+ },
13524
+ "managed_inbox_address": {
13525
+ "type": ["string", "null"],
13526
+ "description": "The managed inbox FQDN to reply as, or null if the org has no managed inbox."
13527
+ },
13528
+ "created_at": {
13529
+ "type": "string",
13530
+ "format": "date-time"
13531
+ },
13532
+ "onboarding_completed": { "type": "boolean" },
13533
+ "onboarding_step": { "type": ["string", "null"] },
13534
+ "stripe_subscription_status": { "type": ["string", "null"] },
13535
+ "subscription_current_period_end": {
13536
+ "type": ["string", "null"],
13537
+ "format": "date-time"
13538
+ },
13539
+ "subscription_cancel_at_period_end": { "type": ["boolean", "null"] },
13540
+ "spam_threshold": {
13541
+ "type": ["number", "null"],
13542
+ "minimum": 0,
13543
+ "maximum": 15
13544
+ },
13545
+ "discard_content_on_webhook_confirmed": { "type": "boolean" },
13546
+ "webhook_secret_rotated_at": {
13547
+ "type": ["string", "null"],
13548
+ "format": "date-time"
13549
+ }
13550
+ },
13551
+ "required": [
13552
+ "id",
13553
+ "email",
13554
+ "plan",
13555
+ "limits",
13556
+ "entitlements",
13557
+ "managed_inbox_address",
13558
+ "created_at",
13559
+ "discard_content_on_webhook_confirmed"
13560
+ ]
13561
+ },
13562
+ "sdkName": "getAccount",
13563
+ "summary": "Get account info",
13564
+ "tag": "Account",
13565
+ "tagCommand": "account"
13566
+ },
13567
+ {
13568
+ "binaryResponse": false,
13569
+ "bodyRequired": false,
13570
+ "command": "get-storage-stats",
13571
+ "description": null,
13572
+ "hasJsonBody": false,
13573
+ "method": "GET",
13574
+ "operationId": "getStorageStats",
13575
+ "path": "/account/storage",
13576
+ "pathParams": [],
13577
+ "queryParams": [],
13578
+ "requestSchema": null,
13579
+ "responseSchema": {
13580
+ "type": "object",
13581
+ "properties": {
13582
+ "used_bytes": {
13583
+ "type": "integer",
13584
+ "description": "Total storage used in bytes"
13585
+ },
13586
+ "used_kb": {
13587
+ "type": "number",
13588
+ "description": "Total storage used in kilobytes (1 decimal)"
13589
+ },
13590
+ "used_mb": {
13591
+ "type": "number",
13592
+ "description": "Total storage used in megabytes (2 decimals)"
13593
+ },
13594
+ "quota_mb": {
13595
+ "type": "number",
13596
+ "description": "Storage quota in megabytes (based on plan)"
13597
+ },
13598
+ "percentage": {
13599
+ "type": "number",
13600
+ "description": "Percentage of quota used (1 decimal)"
13601
+ },
13602
+ "emails_count": {
13603
+ "type": "integer",
13604
+ "description": "Number of stored emails"
13605
+ }
13606
+ },
13607
+ "required": [
13608
+ "used_bytes",
13609
+ "used_kb",
13610
+ "used_mb",
13611
+ "quota_mb",
13612
+ "percentage",
13613
+ "emails_count"
13614
+ ]
13615
+ },
13616
+ "sdkName": "getStorageStats",
13617
+ "summary": "Get storage usage",
13618
+ "tag": "Account",
13619
+ "tagCommand": "account"
13620
+ },
13621
+ {
13622
+ "binaryResponse": false,
13623
+ "bodyRequired": false,
13624
+ "command": "get-webhook-secret",
13625
+ "description": "Returns the webhook signing secret for your account. If no\nsecret exists yet, one is generated automatically on first\naccess.\n\nSigning is account-scoped, not per-endpoint. Every webhook\ndelivery from any of your registered endpoints is signed\nwith this single secret. Rotate via\n`POST /account/webhook-secret/rotate`.\n\n**Secret format**: the returned string looks base64-shaped\n(e.g. `XNHBBW8VqoBjRfNs1tkZj11jTk...`) but is NOT base64.\nUse it AS-IS as a UTF-8 string when computing HMAC over a\ndelivery body. Base64-decoding before HMAC will silently\nproduce mismatched signatures.\n\nSee the API-level \"Webhook signing\" section for the full\nwire format (header name, signed string shape, hash algo,\ntolerance) including a language-agnostic verification\nrecipe.\n",
11795
13626
  "hasJsonBody": false,
11796
13627
  "method": "GET",
11797
- "operationId": "getAccount",
11798
- "path": "/account",
13628
+ "operationId": "getWebhookSecret",
13629
+ "path": "/account/webhook-secret",
13630
+ "pathParams": [],
13631
+ "queryParams": [],
13632
+ "requestSchema": null,
13633
+ "responseSchema": {
13634
+ "type": "object",
13635
+ "properties": { "secret": {
13636
+ "type": "string",
13637
+ "description": "The webhook signing secret value"
13638
+ } },
13639
+ "required": ["secret"]
13640
+ },
13641
+ "sdkName": "getWebhookSecret",
13642
+ "summary": "Get webhook signing secret",
13643
+ "tag": "Account",
13644
+ "tagCommand": "account"
13645
+ },
13646
+ {
13647
+ "binaryResponse": false,
13648
+ "bodyRequired": false,
13649
+ "command": "rotate-webhook-secret",
13650
+ "description": "Generates a new webhook signing secret, replacing the current one.\nRate limited to once per 60 minutes.\n",
13651
+ "hasJsonBody": false,
13652
+ "method": "POST",
13653
+ "operationId": "rotateWebhookSecret",
13654
+ "path": "/account/webhook-secret/rotate",
11799
13655
  "pathParams": [],
11800
13656
  "queryParams": [],
11801
13657
  "requestSchema": null,
13658
+ "responseSchema": {
13659
+ "type": "object",
13660
+ "properties": { "secret": {
13661
+ "type": "string",
13662
+ "description": "The webhook signing secret value"
13663
+ } },
13664
+ "required": ["secret"]
13665
+ },
13666
+ "sdkName": "rotateWebhookSecret",
13667
+ "summary": "Rotate webhook signing secret",
13668
+ "tag": "Account",
13669
+ "tagCommand": "account"
13670
+ },
13671
+ {
13672
+ "binaryResponse": false,
13673
+ "bodyRequired": true,
13674
+ "command": "update-account",
13675
+ "description": null,
13676
+ "hasJsonBody": true,
13677
+ "method": "PATCH",
13678
+ "operationId": "updateAccount",
13679
+ "path": "/account",
13680
+ "pathParams": [],
13681
+ "queryParams": [],
13682
+ "requestSchema": {
13683
+ "type": "object",
13684
+ "additionalProperties": false,
13685
+ "properties": {
13686
+ "spam_threshold": {
13687
+ "type": ["number", "null"],
13688
+ "minimum": 0,
13689
+ "maximum": 15,
13690
+ "description": "Global spam score threshold (0-15). Emails scoring above this are rejected. Set to null to disable."
13691
+ },
13692
+ "discard_content_on_webhook_confirmed": {
13693
+ "type": "boolean",
13694
+ "description": "Whether to discard email content after the webhook endpoint confirms receipt."
13695
+ }
13696
+ },
13697
+ "minProperties": 1
13698
+ },
11802
13699
  "responseSchema": {
11803
13700
  "type": "object",
11804
13701
  "properties": {
@@ -11808,6 +13705,73 @@ const operationManifest = [
11808
13705
  },
11809
13706
  "email": { "type": "string" },
11810
13707
  "plan": { "type": "string" },
13708
+ "spam_threshold": {
13709
+ "type": ["number", "null"],
13710
+ "minimum": 0,
13711
+ "maximum": 15
13712
+ },
13713
+ "discard_content_on_webhook_confirmed": { "type": "boolean" }
13714
+ },
13715
+ "required": [
13716
+ "id",
13717
+ "email",
13718
+ "plan",
13719
+ "discard_content_on_webhook_confirmed"
13720
+ ]
13721
+ },
13722
+ "sdkName": "updateAccount",
13723
+ "summary": "Update account settings",
13724
+ "tag": "Account",
13725
+ "tagCommand": "account"
13726
+ },
13727
+ {
13728
+ "binaryResponse": false,
13729
+ "bodyRequired": true,
13730
+ "command": "create-agent-account",
13731
+ "description": "Creates an emailless agent account without authentication and returns a\none-time API key (prefixed `prim_`) plus a provisioned managed inbox.\nThe account is on the `agent` plan: reply-only (it can send only to\naddresses that have already sent it authenticated mail) with tight send\nlimits. Use the returned `api_key` as a Bearer token on later calls. The\naccount can be upgraded to a full developer account by confirming an\nemail through the claim flow. This endpoint does not require an API key.\n",
13732
+ "hasJsonBody": true,
13733
+ "method": "POST",
13734
+ "operationId": "createAgentAccount",
13735
+ "path": "/agent/accounts",
13736
+ "pathParams": [],
13737
+ "queryParams": [],
13738
+ "requestSchema": {
13739
+ "type": "object",
13740
+ "additionalProperties": false,
13741
+ "properties": {
13742
+ "terms_accepted": {
13743
+ "type": "boolean",
13744
+ "enum": [true],
13745
+ "description": "Must be true to accept the Terms of Service and Privacy Policy."
13746
+ },
13747
+ "device_name": {
13748
+ "type": "string",
13749
+ "minLength": 1,
13750
+ "maxLength": 80,
13751
+ "description": "Optional label for the device or agent creating the account."
13752
+ }
13753
+ },
13754
+ "required": ["terms_accepted"]
13755
+ },
13756
+ "responseSchema": {
13757
+ "type": "object",
13758
+ "properties": {
13759
+ "api_key": {
13760
+ "type": "string",
13761
+ "description": "One-time API key (prefixed `prim_`). Shown once; store it securely."
13762
+ },
13763
+ "org_id": {
13764
+ "type": "string",
13765
+ "format": "uuid"
13766
+ },
13767
+ "address": {
13768
+ "type": ["string", "null"],
13769
+ "description": "Provisioned managed inbox FQDN, or null if the inbox publish was deferred."
13770
+ },
13771
+ "plan": {
13772
+ "type": "string",
13773
+ "enum": ["agent"]
13774
+ },
11811
13775
  "limits": {
11812
13776
  "type": "object",
11813
13777
  "description": "Plan-derived quota limits for an account.",
@@ -11832,261 +13796,286 @@ const operationManifest = [
11832
13796
  "spam_thresholds_per_domain"
11833
13797
  ]
11834
13798
  },
11835
- "entitlements": {
11836
- "type": "array",
11837
- "items": { "type": "string" },
11838
- "description": "Granted org entitlement keys (sorted). A headless caller reads its\ncapabilities here — e.g. an emailless agent seeing only\n[\"send_mail\", \"send_to_known_addresses\"] knows it is reply-only.\n"
11839
- },
11840
- "managed_inbox_address": {
11841
- "type": ["string", "null"],
11842
- "description": "The managed inbox FQDN to reply as, or null if the org has no managed inbox."
11843
- },
11844
- "created_at": {
11845
- "type": "string",
11846
- "format": "date-time"
11847
- },
11848
- "onboarding_completed": { "type": "boolean" },
11849
- "onboarding_step": { "type": ["string", "null"] },
11850
- "stripe_subscription_status": { "type": ["string", "null"] },
11851
- "subscription_current_period_end": {
11852
- "type": ["string", "null"],
11853
- "format": "date-time"
11854
- },
11855
- "subscription_cancel_at_period_end": { "type": ["boolean", "null"] },
11856
- "spam_threshold": {
11857
- "type": ["number", "null"],
11858
- "minimum": 0,
11859
- "maximum": 15
11860
- },
11861
- "discard_content_on_webhook_confirmed": { "type": "boolean" },
11862
- "webhook_secret_rotated_at": {
11863
- "type": ["string", "null"],
11864
- "format": "date-time"
13799
+ "upgrade": {
13800
+ "type": "object",
13801
+ "description": "In-band pointer to the upgrade path for an agent account.",
13802
+ "properties": {
13803
+ "plan": {
13804
+ "type": "string",
13805
+ "enum": ["developer"]
13806
+ },
13807
+ "description": { "type": "string" },
13808
+ "claim_path": { "type": "string" }
13809
+ },
13810
+ "required": [
13811
+ "plan",
13812
+ "description",
13813
+ "claim_path"
13814
+ ]
11865
13815
  }
11866
13816
  },
11867
13817
  "required": [
11868
- "id",
11869
- "email",
13818
+ "api_key",
13819
+ "org_id",
13820
+ "address",
11870
13821
  "plan",
11871
13822
  "limits",
11872
- "entitlements",
11873
- "managed_inbox_address",
11874
- "created_at",
11875
- "discard_content_on_webhook_confirmed"
13823
+ "upgrade"
11876
13824
  ]
11877
13825
  },
11878
- "sdkName": "getAccount",
11879
- "summary": "Get account info",
11880
- "tag": "Account",
11881
- "tagCommand": "account"
13826
+ "sdkName": "createAgentAccount",
13827
+ "summary": "Create an emailless agent account",
13828
+ "tag": "Agent",
13829
+ "tagCommand": "agent"
11882
13830
  },
11883
13831
  {
11884
13832
  "binaryResponse": false,
11885
13833
  "bodyRequired": false,
11886
- "command": "get-storage-stats",
11887
- "description": null,
11888
- "hasJsonBody": false,
11889
- "method": "GET",
11890
- "operationId": "getStorageStats",
11891
- "path": "/account/storage",
13834
+ "command": "create-agent-claim-link",
13835
+ "description": "Mints an opaque, single-use link an agent can hand to a human to\ncomplete the email-confirmation upgrade in a browser. Authenticated by\nthe agent's own API key. `claim_url` is null when the API host cannot\nresolve a web origin to build the link.\n",
13836
+ "hasJsonBody": true,
13837
+ "method": "POST",
13838
+ "operationId": "createAgentClaimLink",
13839
+ "path": "/agent/claim/link",
13840
+ "pathParams": [],
13841
+ "queryParams": [],
13842
+ "requestSchema": {
13843
+ "type": "object",
13844
+ "additionalProperties": false,
13845
+ "description": "No fields; an empty object is accepted.",
13846
+ "properties": {}
13847
+ },
13848
+ "responseSchema": {
13849
+ "type": "object",
13850
+ "properties": {
13851
+ "claim_token": { "type": "string" },
13852
+ "claim_url": {
13853
+ "type": ["string", "null"],
13854
+ "description": "Browser URL to hand to a human, or null if no web origin is configured."
13855
+ },
13856
+ "expires_in_seconds": { "type": "integer" }
13857
+ },
13858
+ "required": [
13859
+ "claim_token",
13860
+ "claim_url",
13861
+ "expires_in_seconds"
13862
+ ]
13863
+ },
13864
+ "sdkName": "createAgentClaimLink",
13865
+ "summary": "Create a browser claim link",
13866
+ "tag": "Agent",
13867
+ "tagCommand": "agent"
13868
+ },
13869
+ {
13870
+ "binaryResponse": false,
13871
+ "bodyRequired": true,
13872
+ "command": "resend-agent-signup-verification",
13873
+ "description": "Sends a new email verification code for a pending agent signup session.\nThis endpoint does not require an API key.\n",
13874
+ "hasJsonBody": true,
13875
+ "method": "POST",
13876
+ "operationId": "resendAgentSignupVerification",
13877
+ "path": "/agent/signup/resend",
11892
13878
  "pathParams": [],
11893
13879
  "queryParams": [],
11894
- "requestSchema": null,
13880
+ "requestSchema": {
13881
+ "type": "object",
13882
+ "additionalProperties": false,
13883
+ "properties": { "signup_token": {
13884
+ "type": "string",
13885
+ "minLength": 1
13886
+ } },
13887
+ "required": ["signup_token"]
13888
+ },
11895
13889
  "responseSchema": {
11896
13890
  "type": "object",
11897
13891
  "properties": {
11898
- "used_bytes": {
11899
- "type": "integer",
11900
- "description": "Total storage used in bytes"
11901
- },
11902
- "used_kb": {
11903
- "type": "number",
11904
- "description": "Total storage used in kilobytes (1 decimal)"
11905
- },
11906
- "used_mb": {
11907
- "type": "number",
11908
- "description": "Total storage used in megabytes (2 decimals)"
13892
+ "email": {
13893
+ "type": "string",
13894
+ "format": "email"
11909
13895
  },
11910
- "quota_mb": {
11911
- "type": "number",
11912
- "description": "Storage quota in megabytes (based on plan)"
13896
+ "expires_in": {
13897
+ "type": "integer",
13898
+ "description": "Seconds until the pending signup expires"
11913
13899
  },
11914
- "percentage": {
11915
- "type": "number",
11916
- "description": "Percentage of quota used (1 decimal)"
13900
+ "resend_after": {
13901
+ "type": "integer",
13902
+ "description": "Minimum seconds before requesting another verification email"
11917
13903
  },
11918
- "emails_count": {
13904
+ "verification_code_length": {
11919
13905
  "type": "integer",
11920
- "description": "Number of stored emails"
13906
+ "description": "Number of digits in the emailed verification code"
11921
13907
  }
11922
13908
  },
11923
13909
  "required": [
11924
- "used_bytes",
11925
- "used_kb",
11926
- "used_mb",
11927
- "quota_mb",
11928
- "percentage",
11929
- "emails_count"
13910
+ "email",
13911
+ "expires_in",
13912
+ "resend_after",
13913
+ "verification_code_length"
11930
13914
  ]
11931
13915
  },
11932
- "sdkName": "getStorageStats",
11933
- "summary": "Get storage usage",
11934
- "tag": "Account",
11935
- "tagCommand": "account"
13916
+ "sdkName": "resendAgentSignupVerification",
13917
+ "summary": "Resend agent signup verification code",
13918
+ "tag": "Agent",
13919
+ "tagCommand": "agent"
11936
13920
  },
11937
13921
  {
11938
13922
  "binaryResponse": false,
11939
- "bodyRequired": false,
11940
- "command": "get-webhook-secret",
11941
- "description": "Returns the webhook signing secret for your account. If no\nsecret exists yet, one is generated automatically on first\naccess.\n\nSigning is account-scoped, not per-endpoint. Every webhook\ndelivery from any of your registered endpoints is signed\nwith this single secret. Rotate via\n`POST /account/webhook-secret/rotate`.\n\n**Secret format**: the returned string looks base64-shaped\n(e.g. `XNHBBW8VqoBjRfNs1tkZj11jTk...`) but is NOT base64.\nUse it AS-IS as a UTF-8 string when computing HMAC over a\ndelivery body. Base64-decoding before HMAC will silently\nproduce mismatched signatures.\n\nSee the API-level \"Webhook signing\" section for the full\nwire format (header name, signed string shape, hash algo,\ntolerance) including a language-agnostic verification\nrecipe.\n",
11942
- "hasJsonBody": false,
11943
- "method": "GET",
11944
- "operationId": "getWebhookSecret",
11945
- "path": "/account/webhook-secret",
13923
+ "bodyRequired": true,
13924
+ "command": "start-agent-claim",
13925
+ "description": "Begins upgrading an emailless `agent` account into a full `developer`\naccount by confirming an email address. Authenticated by the agent's own\nAPI key (the org is taken from the credential). Sends a verification\ncode to the supplied email and returns the claim session id plus resend\ntiming. Submit the code to `/agent/claim/verify` to complete the\nupgrade. Confirming an email that already belongs to a Primitive account\nis rejected.\n",
13926
+ "hasJsonBody": true,
13927
+ "method": "POST",
13928
+ "operationId": "startAgentClaim",
13929
+ "path": "/agent/claim/start",
11946
13930
  "pathParams": [],
11947
13931
  "queryParams": [],
11948
- "requestSchema": null,
11949
- "responseSchema": {
13932
+ "requestSchema": {
11950
13933
  "type": "object",
11951
- "properties": { "secret": {
13934
+ "additionalProperties": false,
13935
+ "properties": { "email": {
11952
13936
  "type": "string",
11953
- "description": "The webhook signing secret value"
13937
+ "format": "email",
13938
+ "maxLength": 254,
13939
+ "description": "Email to confirm. Must not already belong to a Primitive account."
11954
13940
  } },
11955
- "required": ["secret"]
13941
+ "required": ["email"]
11956
13942
  },
11957
- "sdkName": "getWebhookSecret",
11958
- "summary": "Get webhook signing secret",
11959
- "tag": "Account",
11960
- "tagCommand": "account"
11961
- },
11962
- {
11963
- "binaryResponse": false,
11964
- "bodyRequired": false,
11965
- "command": "rotate-webhook-secret",
11966
- "description": "Generates a new webhook signing secret, replacing the current one.\nRate limited to once per 60 minutes.\n",
11967
- "hasJsonBody": false,
11968
- "method": "POST",
11969
- "operationId": "rotateWebhookSecret",
11970
- "path": "/account/webhook-secret/rotate",
11971
- "pathParams": [],
11972
- "queryParams": [],
11973
- "requestSchema": null,
11974
13943
  "responseSchema": {
11975
13944
  "type": "object",
11976
- "properties": { "secret": {
11977
- "type": "string",
11978
- "description": "The webhook signing secret value"
11979
- } },
11980
- "required": ["secret"]
13945
+ "properties": {
13946
+ "claim_session_id": { "type": "string" },
13947
+ "resend_after_seconds": { "type": "integer" },
13948
+ "expires_in_seconds": { "type": "integer" }
13949
+ },
13950
+ "required": [
13951
+ "claim_session_id",
13952
+ "resend_after_seconds",
13953
+ "expires_in_seconds"
13954
+ ]
11981
13955
  },
11982
- "sdkName": "rotateWebhookSecret",
11983
- "summary": "Rotate webhook signing secret",
11984
- "tag": "Account",
11985
- "tagCommand": "account"
13956
+ "sdkName": "startAgentClaim",
13957
+ "summary": "Start an agent account email claim",
13958
+ "tag": "Agent",
13959
+ "tagCommand": "agent"
11986
13960
  },
11987
13961
  {
11988
13962
  "binaryResponse": false,
11989
13963
  "bodyRequired": true,
11990
- "command": "update-account",
11991
- "description": null,
13964
+ "command": "start-agent-signup",
13965
+ "description": "Starts an agent-native signup session. `signup_code` is optional;\nomit it to sign up without one. The API creates a pending signup\nsession, sends an email verification code, and returns an opaque\nsignup token used by the resend and verify steps. This endpoint\ndoes not require an API key.\n",
11992
13966
  "hasJsonBody": true,
11993
- "method": "PATCH",
11994
- "operationId": "updateAccount",
11995
- "path": "/account",
13967
+ "method": "POST",
13968
+ "operationId": "startAgentSignup",
13969
+ "path": "/agent/signup/start",
11996
13970
  "pathParams": [],
11997
13971
  "queryParams": [],
11998
13972
  "requestSchema": {
11999
13973
  "type": "object",
12000
13974
  "additionalProperties": false,
12001
13975
  "properties": {
12002
- "spam_threshold": {
12003
- "type": ["number", "null"],
12004
- "minimum": 0,
12005
- "maximum": 15,
12006
- "description": "Global spam score threshold (0-15). Emails scoring above this are rejected. Set to null to disable."
13976
+ "email": {
13977
+ "type": "string",
13978
+ "format": "email",
13979
+ "maxLength": 254
12007
13980
  },
12008
- "discard_content_on_webhook_confirmed": {
13981
+ "signup_code": {
13982
+ "type": "string",
13983
+ "minLength": 1,
13984
+ "maxLength": 128,
13985
+ "description": "Optional signup code. Omit if you do not have one."
13986
+ },
13987
+ "terms_accepted": {
12009
13988
  "type": "boolean",
12010
- "description": "Whether to discard email content after the webhook endpoint confirms receipt."
13989
+ "const": true,
13990
+ "description": "Must be true to confirm acceptance of Primitive's Terms of Service and Privacy Policy"
13991
+ },
13992
+ "device_name": {
13993
+ "type": "string",
13994
+ "minLength": 1,
13995
+ "maxLength": 80,
13996
+ "description": "Human-readable device name used for the created agent OAuth session"
13997
+ },
13998
+ "metadata": {
13999
+ "type": "object",
14000
+ "additionalProperties": true,
14001
+ "description": "Optional client metadata stored with the signup session; serialized JSON must be 2048 bytes or fewer"
12011
14002
  }
12012
14003
  },
12013
- "minProperties": 1
14004
+ "required": ["email", "terms_accepted"]
12014
14005
  },
12015
14006
  "responseSchema": {
12016
14007
  "type": "object",
12017
14008
  "properties": {
12018
- "id": {
14009
+ "signup_token": {
12019
14010
  "type": "string",
12020
- "format": "uuid"
14011
+ "description": "Opaque token used to verify or resend the pending agent signup"
12021
14012
  },
12022
- "email": { "type": "string" },
12023
- "plan": { "type": "string" },
12024
- "spam_threshold": {
12025
- "type": ["number", "null"],
12026
- "minimum": 0,
12027
- "maximum": 15
14013
+ "email": {
14014
+ "type": "string",
14015
+ "format": "email"
12028
14016
  },
12029
- "discard_content_on_webhook_confirmed": { "type": "boolean" }
14017
+ "expires_in": {
14018
+ "type": "integer",
14019
+ "description": "Seconds until the pending signup expires"
14020
+ },
14021
+ "resend_after": {
14022
+ "type": "integer",
14023
+ "description": "Minimum seconds before requesting another verification email"
14024
+ },
14025
+ "verification_code_length": {
14026
+ "type": "integer",
14027
+ "description": "Number of digits in the emailed verification code"
14028
+ }
12030
14029
  },
12031
14030
  "required": [
12032
- "id",
14031
+ "signup_token",
12033
14032
  "email",
12034
- "plan",
12035
- "discard_content_on_webhook_confirmed"
14033
+ "expires_in",
14034
+ "resend_after",
14035
+ "verification_code_length"
12036
14036
  ]
12037
14037
  },
12038
- "sdkName": "updateAccount",
12039
- "summary": "Update account settings",
12040
- "tag": "Account",
12041
- "tagCommand": "account"
14038
+ "sdkName": "startAgentSignup",
14039
+ "summary": "Start agent account signup",
14040
+ "tag": "Agent",
14041
+ "tagCommand": "agent"
12042
14042
  },
12043
14043
  {
12044
14044
  "binaryResponse": false,
12045
14045
  "bodyRequired": true,
12046
- "command": "create-agent-account",
12047
- "description": "Creates an emailless agent account without authentication and returns a\none-time API key (prefixed `prim_`) plus a provisioned managed inbox.\nThe account is on the `agent` plan: reply-only (it can send only to\naddresses that have already sent it authenticated mail) with tight send\nlimits. Use the returned `api_key` as a Bearer token on later calls. The\naccount can be upgraded to a full developer account by confirming an\nemail through the claim flow. This endpoint does not require an API key.\n",
14046
+ "command": "verify-agent-claim",
14047
+ "description": "Confirms the verification code emailed by `/agent/claim/start` and\nupgrades the account to the `developer` plan. The org id, API key, and\nmanaged inbox all carry over; the send cap lifts. Authenticated by the\nagent's own API key.\n",
12048
14048
  "hasJsonBody": true,
12049
14049
  "method": "POST",
12050
- "operationId": "createAgentAccount",
12051
- "path": "/agent/accounts",
14050
+ "operationId": "verifyAgentClaim",
14051
+ "path": "/agent/claim/verify",
12052
14052
  "pathParams": [],
12053
14053
  "queryParams": [],
12054
14054
  "requestSchema": {
12055
14055
  "type": "object",
12056
14056
  "additionalProperties": false,
12057
- "properties": {
12058
- "terms_accepted": {
12059
- "type": "boolean",
12060
- "enum": [true],
12061
- "description": "Must be true to accept the Terms of Service and Privacy Policy."
12062
- },
12063
- "device_name": {
12064
- "type": "string",
12065
- "minLength": 1,
12066
- "maxLength": 80,
12067
- "description": "Optional label for the device or agent creating the account."
12068
- }
12069
- },
12070
- "required": ["terms_accepted"]
14057
+ "properties": { "verification_code": {
14058
+ "type": "string",
14059
+ "minLength": 1,
14060
+ "maxLength": 32,
14061
+ "description": "The verification code emailed by the claim start step."
14062
+ } },
14063
+ "required": ["verification_code"]
12071
14064
  },
12072
14065
  "responseSchema": {
12073
14066
  "type": "object",
12074
14067
  "properties": {
12075
- "api_key": {
12076
- "type": "string",
12077
- "description": "One-time API key (prefixed `prim_`). Shown once; store it securely."
12078
- },
12079
14068
  "org_id": {
12080
14069
  "type": "string",
12081
- "format": "uuid"
12082
- },
12083
- "address": {
12084
- "type": ["string", "null"],
12085
- "description": "Provisioned managed inbox FQDN, or null if the inbox publish was deferred."
14070
+ "format": "uuid"
12086
14071
  },
12087
14072
  "plan": {
12088
14073
  "type": "string",
12089
- "enum": ["agent"]
14074
+ "enum": ["developer"]
14075
+ },
14076
+ "email": {
14077
+ "type": "string",
14078
+ "format": "email"
12090
14079
  },
12091
14080
  "limits": {
12092
14081
  "type": "object",
@@ -12111,545 +14100,675 @@ const operationManifest = [
12111
14100
  "filters_per_domain",
12112
14101
  "spam_thresholds_per_domain"
12113
14102
  ]
12114
- },
12115
- "upgrade": {
12116
- "type": "object",
12117
- "description": "In-band pointer to the upgrade path for an agent account.",
12118
- "properties": {
12119
- "plan": {
12120
- "type": "string",
12121
- "enum": ["developer"]
12122
- },
12123
- "description": { "type": "string" },
12124
- "claim_path": { "type": "string" }
12125
- },
12126
- "required": [
12127
- "plan",
12128
- "description",
12129
- "claim_path"
12130
- ]
12131
14103
  }
12132
14104
  },
12133
14105
  "required": [
12134
- "api_key",
12135
14106
  "org_id",
12136
- "address",
12137
14107
  "plan",
12138
- "limits",
12139
- "upgrade"
14108
+ "email",
14109
+ "limits"
12140
14110
  ]
12141
14111
  },
12142
- "sdkName": "createAgentAccount",
12143
- "summary": "Create an emailless agent account",
14112
+ "sdkName": "verifyAgentClaim",
14113
+ "summary": "Verify an agent account email claim",
12144
14114
  "tag": "Agent",
12145
14115
  "tagCommand": "agent"
12146
14116
  },
12147
14117
  {
12148
14118
  "binaryResponse": false,
12149
- "bodyRequired": false,
12150
- "command": "create-agent-claim-link",
12151
- "description": "Mints an opaque, single-use link an agent can hand to a human to\ncomplete the email-confirmation upgrade in a browser. Authenticated by\nthe agent's own API key. `claim_url` is null when the API host cannot\nresolve a web origin to build the link.\n",
14119
+ "bodyRequired": true,
14120
+ "command": "verify-agent-signup",
14121
+ "description": "Verifies the email code for an agent signup session and creates\nthe account when needed. When the session was started with a\n`signup_code`, the reserved code is redeemed; sessions started\nwithout a code skip the redemption step. An org-scoped OAuth\nsession for CLI authentication is minted and the raw tokens are\nreturned exactly once. For existing users, the optional `org_id`\nselects which accessible workspace should receive the new\nsession (no signup-code redemption is performed for existing\nusers regardless of how the session was started).\n",
12152
14122
  "hasJsonBody": true,
12153
14123
  "method": "POST",
12154
- "operationId": "createAgentClaimLink",
12155
- "path": "/agent/claim/link",
14124
+ "operationId": "verifyAgentSignup",
14125
+ "path": "/agent/signup/verify",
12156
14126
  "pathParams": [],
12157
14127
  "queryParams": [],
12158
14128
  "requestSchema": {
12159
14129
  "type": "object",
12160
14130
  "additionalProperties": false,
12161
- "description": "No fields; an empty object is accepted.",
12162
- "properties": {}
14131
+ "properties": {
14132
+ "signup_token": {
14133
+ "type": "string",
14134
+ "minLength": 1
14135
+ },
14136
+ "verification_code": {
14137
+ "type": "string",
14138
+ "minLength": 1,
14139
+ "maxLength": 32
14140
+ },
14141
+ "org_id": {
14142
+ "type": "string",
14143
+ "format": "uuid",
14144
+ "description": "Optional workspace id to target when the verified email already belongs to multiple workspaces"
14145
+ }
14146
+ },
14147
+ "required": ["signup_token", "verification_code"]
12163
14148
  },
12164
14149
  "responseSchema": {
12165
14150
  "type": "object",
12166
14151
  "properties": {
12167
- "claim_token": { "type": "string" },
12168
- "claim_url": {
12169
- "type": ["string", "null"],
12170
- "description": "Browser URL to hand to a human, or null if no web origin is configured."
14152
+ "api_key": {
14153
+ "type": "string",
14154
+ "description": "Legacy alias for access_token. New CLI builds should persist access_token and refresh_token."
12171
14155
  },
12172
- "expires_in_seconds": { "type": "integer" }
14156
+ "key_id": {
14157
+ "type": "string",
14158
+ "format": "uuid",
14159
+ "description": "Legacy alias for oauth_grant_id"
14160
+ },
14161
+ "key_prefix": {
14162
+ "type": "string",
14163
+ "description": "Legacy display prefix derived from access_token"
14164
+ },
14165
+ "access_token": {
14166
+ "type": "string",
14167
+ "description": "OAuth access token for CLI API authentication"
14168
+ },
14169
+ "refresh_token": {
14170
+ "type": "string",
14171
+ "description": "OAuth refresh token used by the CLI to renew access"
14172
+ },
14173
+ "token_type": {
14174
+ "type": "string",
14175
+ "enum": ["Bearer"]
14176
+ },
14177
+ "expires_in": {
14178
+ "type": "integer",
14179
+ "description": "Seconds until access_token expires"
14180
+ },
14181
+ "auth_method": {
14182
+ "type": "string",
14183
+ "enum": ["oauth"]
14184
+ },
14185
+ "oauth_grant_id": {
14186
+ "type": "string",
14187
+ "format": "uuid"
14188
+ },
14189
+ "oauth_client_id": { "type": "string" },
14190
+ "org_id": {
14191
+ "type": "string",
14192
+ "format": "uuid"
14193
+ },
14194
+ "org_name": { "type": ["string", "null"] },
14195
+ "orgs": {
14196
+ "type": "array",
14197
+ "items": {
14198
+ "type": "object",
14199
+ "properties": {
14200
+ "id": {
14201
+ "type": "string",
14202
+ "format": "uuid"
14203
+ },
14204
+ "name": { "type": ["string", "null"] }
14205
+ },
14206
+ "required": ["id", "name"]
14207
+ },
14208
+ "description": "Workspaces available to the verified email. The minted session targets `org_id`."
14209
+ }
12173
14210
  },
12174
14211
  "required": [
12175
- "claim_token",
12176
- "claim_url",
12177
- "expires_in_seconds"
14212
+ "api_key",
14213
+ "key_id",
14214
+ "key_prefix",
14215
+ "access_token",
14216
+ "refresh_token",
14217
+ "token_type",
14218
+ "expires_in",
14219
+ "auth_method",
14220
+ "oauth_grant_id",
14221
+ "oauth_client_id",
14222
+ "org_id",
14223
+ "org_name",
14224
+ "orgs"
12178
14225
  ]
12179
14226
  },
12180
- "sdkName": "createAgentClaimLink",
12181
- "summary": "Create a browser claim link",
14227
+ "sdkName": "verifyAgentSignup",
14228
+ "summary": "Verify agent signup and create OAuth tokens",
12182
14229
  "tag": "Agent",
12183
14230
  "tagCommand": "agent"
12184
14231
  },
12185
14232
  {
12186
14233
  "binaryResponse": false,
12187
14234
  "bodyRequired": true,
12188
- "command": "resend-agent-signup-verification",
12189
- "description": "Sends a new email verification code for a pending agent signup session.\nThis endpoint does not require an API key.\n",
14235
+ "command": "claim-agent-connection",
14236
+ "description": "Address-bound external runtime pairing. Management operations require an organization owner or admin session or OAuth token; members and organization API keys cannot manage connections. Claim is authorized only by its one-use invitation. Connected means a real challenge was received and a reply sent by the current bound credential was received back. Invitations expire after 15 minutes. Reconnection preserves the address and revokes previous credentials. Status responses contain no credentials. Runtime credentials allow only address-scoped mail operations, organization note reads and own-address note writes. The credential is returned once. If the claim response is lost or the outcome is unknown, request a fresh owner invitation instead of retrying the consumed invitation.",
12190
14237
  "hasJsonBody": true,
12191
14238
  "method": "POST",
12192
- "operationId": "resendAgentSignupVerification",
12193
- "path": "/agent/signup/resend",
14239
+ "operationId": "claimAgentConnection",
14240
+ "path": "/agent-connections/claim",
12194
14241
  "pathParams": [],
12195
14242
  "queryParams": [],
12196
14243
  "requestSchema": {
14244
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
12197
14245
  "type": "object",
12198
- "additionalProperties": false,
12199
- "properties": { "signup_token": {
14246
+ "properties": { "token": {
12200
14247
  "type": "string",
12201
- "minLength": 1
14248
+ "minLength": 32,
14249
+ "maxLength": 256
12202
14250
  } },
12203
- "required": ["signup_token"]
14251
+ "required": ["token"],
14252
+ "additionalProperties": false
12204
14253
  },
12205
14254
  "responseSchema": {
12206
14255
  "type": "object",
14256
+ "required": ["success", "data"],
12207
14257
  "properties": {
12208
- "email": {
12209
- "type": "string",
12210
- "format": "email"
12211
- },
12212
- "expires_in": {
12213
- "type": "integer",
12214
- "description": "Seconds until the pending signup expires"
14258
+ "success": {
14259
+ "const": true,
14260
+ "type": "boolean"
12215
14261
  },
12216
- "resend_after": {
12217
- "type": "integer",
12218
- "description": "Minimum seconds before requesting another verification email"
14262
+ "data": {
14263
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
14264
+ "type": "object",
14265
+ "properties": {
14266
+ "connection": {
14267
+ "type": "object",
14268
+ "properties": {
14269
+ "address": {
14270
+ "type": "string",
14271
+ "format": "email",
14272
+ "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
14273
+ },
14274
+ "name": {
14275
+ "type": "string",
14276
+ "minLength": 1,
14277
+ "maxLength": 80
14278
+ },
14279
+ "owner_address": {
14280
+ "type": "string",
14281
+ "format": "email",
14282
+ "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
14283
+ },
14284
+ "status": {
14285
+ "type": "string",
14286
+ "enum": [
14287
+ "pending",
14288
+ "claimed",
14289
+ "connected",
14290
+ "revoked"
14291
+ ]
14292
+ },
14293
+ "created_at": {
14294
+ "type": "string",
14295
+ "format": "date-time",
14296
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
14297
+ },
14298
+ "updated_at": {
14299
+ "type": "string",
14300
+ "format": "date-time",
14301
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
14302
+ },
14303
+ "claimed_at": { "anyOf": [{
14304
+ "type": "string",
14305
+ "format": "date-time",
14306
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
14307
+ }, { "type": "null" }] },
14308
+ "verified_at": { "anyOf": [{
14309
+ "type": "string",
14310
+ "format": "date-time",
14311
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
14312
+ }, { "type": "null" }] },
14313
+ "last_seen_at": { "anyOf": [{
14314
+ "type": "string",
14315
+ "format": "date-time",
14316
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
14317
+ }, { "type": "null" }] }
14318
+ },
14319
+ "required": [
14320
+ "address",
14321
+ "name",
14322
+ "owner_address",
14323
+ "status",
14324
+ "created_at",
14325
+ "updated_at",
14326
+ "claimed_at",
14327
+ "verified_at",
14328
+ "last_seen_at"
14329
+ ],
14330
+ "additionalProperties": false
14331
+ },
14332
+ "org_id": {
14333
+ "type": "string",
14334
+ "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
14335
+ },
14336
+ "owner_address": {
14337
+ "type": "string",
14338
+ "format": "email",
14339
+ "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
14340
+ },
14341
+ "api_key": { "type": "string" },
14342
+ "api_base_url": {
14343
+ "type": "string",
14344
+ "format": "uri"
14345
+ }
14346
+ },
14347
+ "required": [
14348
+ "connection",
14349
+ "org_id",
14350
+ "owner_address",
14351
+ "api_key",
14352
+ "api_base_url"
14353
+ ],
14354
+ "additionalProperties": false
14355
+ }
14356
+ }
14357
+ },
14358
+ "sdkName": "claimAgentConnection",
14359
+ "summary": "claim Agent Connection",
14360
+ "tag": "Agent Connections",
14361
+ "tagCommand": "agent-connections"
14362
+ },
14363
+ {
14364
+ "binaryResponse": false,
14365
+ "bodyRequired": false,
14366
+ "command": "remove-agent-connection",
14367
+ "description": "Permanently removes a revoked connection record. Requires an organization\nowner or admin session or OAuth token; organization API keys are denied.\nDisconnect first using DELETE /agent-connections/{address}. An active\nconnection returns 409 connection_not_revoked. Missing or already removed\nrecords return 404. Mail, address notes, domains and external runtimes are\npreserved. The same address can be paired again with a new invitation;\nold credentials and invitations remain invalid.\n",
14368
+ "hasJsonBody": false,
14369
+ "method": "POST",
14370
+ "operationId": "removeAgentConnection",
14371
+ "path": "/agent-connections/{address}/remove",
14372
+ "pathParams": [{
14373
+ "description": "The email address identifying the revoked connection.",
14374
+ "enum": null,
14375
+ "name": "address",
14376
+ "required": true,
14377
+ "type": "string"
14378
+ }],
14379
+ "queryParams": [],
14380
+ "requestSchema": null,
14381
+ "responseSchema": {
14382
+ "type": "object",
14383
+ "required": ["success", "data"],
14384
+ "properties": {
14385
+ "success": {
14386
+ "type": "boolean",
14387
+ "const": true
12219
14388
  },
12220
- "verification_code_length": {
12221
- "type": "integer",
12222
- "description": "Number of digits in the emailed verification code"
14389
+ "data": {
14390
+ "type": "object",
14391
+ "required": ["deleted"],
14392
+ "properties": { "deleted": {
14393
+ "type": "boolean",
14394
+ "const": true
14395
+ } }
12223
14396
  }
12224
- },
12225
- "required": [
12226
- "email",
12227
- "expires_in",
12228
- "resend_after",
12229
- "verification_code_length"
12230
- ]
14397
+ }
12231
14398
  },
12232
- "sdkName": "resendAgentSignupVerification",
12233
- "summary": "Resend agent signup verification code",
12234
- "tag": "Agent",
12235
- "tagCommand": "agent"
14399
+ "sdkName": "removeAgentConnection",
14400
+ "summary": "Remove a revoked agent connection",
14401
+ "tag": "Agent Connections",
14402
+ "tagCommand": "agent-connections"
12236
14403
  },
12237
14404
  {
12238
14405
  "binaryResponse": false,
12239
- "bodyRequired": true,
12240
- "command": "start-agent-claim",
12241
- "description": "Begins upgrading an emailless `agent` account into a full `developer`\naccount by confirming an email address. Authenticated by the agent's own\nAPI key (the org is taken from the credential). Sends a verification\ncode to the supplied email and returns the claim session id plus resend\ntiming. Submit the code to `/agent/claim/verify` to complete the\nupgrade. Confirming an email that already belongs to a Primitive account\nis rejected.\n",
14406
+ "bodyRequired": false,
14407
+ "command": "cli-logout",
14408
+ "description": "Revokes the OAuth grant used to authenticate the request. API-key\nauthenticated legacy logout requests succeed without deleting server API\nkeys so old local CLI state can be cleared safely.\n",
12242
14409
  "hasJsonBody": true,
12243
14410
  "method": "POST",
12244
- "operationId": "startAgentClaim",
12245
- "path": "/agent/claim/start",
14411
+ "operationId": "cliLogout",
14412
+ "path": "/cli/logout",
12246
14413
  "pathParams": [],
12247
14414
  "queryParams": [],
12248
14415
  "requestSchema": {
12249
14416
  "type": "object",
12250
14417
  "additionalProperties": false,
12251
- "properties": { "email": {
14418
+ "properties": { "key_id": {
12252
14419
  "type": "string",
12253
- "format": "email",
12254
- "maxLength": 254,
12255
- "description": "Email to confirm. Must not already belong to a Primitive account."
12256
- } },
12257
- "required": ["email"]
14420
+ "format": "uuid",
14421
+ "description": "Optional id guard; when provided it must match the authenticated OAuth grant id or API key id"
14422
+ } }
12258
14423
  },
12259
14424
  "responseSchema": {
12260
14425
  "type": "object",
12261
14426
  "properties": {
12262
- "claim_session_id": { "type": "string" },
12263
- "resend_after_seconds": { "type": "integer" },
12264
- "expires_in_seconds": { "type": "integer" }
14427
+ "revoked": {
14428
+ "type": "boolean",
14429
+ "description": "True when an OAuth grant was revoked. False for API-key-authenticated legacy logout, which only clears local CLI state."
14430
+ },
14431
+ "key_id": {
14432
+ "type": "string",
14433
+ "format": "uuid",
14434
+ "description": "API key id for API-key-authenticated legacy logout"
14435
+ },
14436
+ "oauth_grant_id": {
14437
+ "type": "string",
14438
+ "format": "uuid",
14439
+ "description": "OAuth grant id revoked by OAuth-authenticated logout"
14440
+ }
12265
14441
  },
12266
- "required": [
12267
- "claim_session_id",
12268
- "resend_after_seconds",
12269
- "expires_in_seconds"
12270
- ]
14442
+ "required": ["revoked"]
12271
14443
  },
12272
- "sdkName": "startAgentClaim",
12273
- "summary": "Start an agent account email claim",
12274
- "tag": "Agent",
12275
- "tagCommand": "agent"
14444
+ "sdkName": "cliLogout",
14445
+ "summary": "Revoke the current CLI OAuth session",
14446
+ "tag": "CLI",
14447
+ "tagCommand": "cli"
12276
14448
  },
12277
14449
  {
12278
14450
  "binaryResponse": false,
12279
14451
  "bodyRequired": true,
12280
- "command": "start-agent-signup",
12281
- "description": "Starts an agent-native signup session. `signup_code` is optional;\nomit it to sign up without one. The API creates a pending signup\nsession, sends an email verification code, and returns an opaque\nsignup token used by the resend and verify steps. This endpoint\ndoes not require an API key.\n",
14452
+ "command": "poll-cli-login",
14453
+ "description": "Polls a CLI login session until the browser approval either succeeds,\nis denied, expires, or is polled too quickly. The OAuth token set is\ncreated only after approval and is returned exactly once.\n",
12282
14454
  "hasJsonBody": true,
12283
14455
  "method": "POST",
12284
- "operationId": "startAgentSignup",
12285
- "path": "/agent/signup/start",
14456
+ "operationId": "pollCliLogin",
14457
+ "path": "/cli/login/poll",
12286
14458
  "pathParams": [],
12287
14459
  "queryParams": [],
12288
14460
  "requestSchema": {
12289
14461
  "type": "object",
12290
14462
  "additionalProperties": false,
14463
+ "properties": { "device_code": {
14464
+ "type": "string",
14465
+ "minLength": 1
14466
+ } },
14467
+ "required": ["device_code"]
14468
+ },
14469
+ "responseSchema": {
14470
+ "type": "object",
12291
14471
  "properties": {
12292
- "email": {
14472
+ "api_key": {
12293
14473
  "type": "string",
12294
- "format": "email",
12295
- "maxLength": 254
14474
+ "description": "Legacy alias for access_token. New CLI builds should persist access_token and refresh_token."
12296
14475
  },
12297
- "signup_code": {
14476
+ "key_id": {
12298
14477
  "type": "string",
12299
- "minLength": 1,
12300
- "maxLength": 128,
12301
- "description": "Optional signup code. Omit if you do not have one."
14478
+ "format": "uuid",
14479
+ "description": "Legacy alias for oauth_grant_id"
12302
14480
  },
12303
- "terms_accepted": {
12304
- "type": "boolean",
12305
- "const": true,
12306
- "description": "Must be true to confirm acceptance of Primitive's Terms of Service and Privacy Policy"
14481
+ "key_prefix": {
14482
+ "type": "string",
14483
+ "description": "Legacy display prefix derived from access_token"
12307
14484
  },
12308
- "device_name": {
14485
+ "access_token": {
12309
14486
  "type": "string",
12310
- "minLength": 1,
12311
- "maxLength": 80,
12312
- "description": "Human-readable device name used for the created agent OAuth session"
14487
+ "description": "OAuth access token for CLI API authentication"
12313
14488
  },
12314
- "metadata": {
12315
- "type": "object",
12316
- "additionalProperties": true,
12317
- "description": "Optional client metadata stored with the signup session; serialized JSON must be 2048 bytes or fewer"
12318
- }
12319
- },
12320
- "required": ["email", "terms_accepted"]
12321
- },
12322
- "responseSchema": {
12323
- "type": "object",
12324
- "properties": {
12325
- "signup_token": {
14489
+ "refresh_token": {
12326
14490
  "type": "string",
12327
- "description": "Opaque token used to verify or resend the pending agent signup"
14491
+ "description": "OAuth refresh token used by the CLI to renew access"
12328
14492
  },
12329
- "email": {
14493
+ "token_type": {
12330
14494
  "type": "string",
12331
- "format": "email"
14495
+ "enum": ["Bearer"]
12332
14496
  },
12333
14497
  "expires_in": {
12334
14498
  "type": "integer",
12335
- "description": "Seconds until the pending signup expires"
14499
+ "description": "Seconds until access_token expires"
12336
14500
  },
12337
- "resend_after": {
12338
- "type": "integer",
12339
- "description": "Minimum seconds before requesting another verification email"
14501
+ "auth_method": {
14502
+ "type": "string",
14503
+ "enum": ["oauth"]
12340
14504
  },
12341
- "verification_code_length": {
12342
- "type": "integer",
12343
- "description": "Number of digits in the emailed verification code"
12344
- }
14505
+ "oauth_grant_id": {
14506
+ "type": "string",
14507
+ "format": "uuid"
14508
+ },
14509
+ "oauth_client_id": { "type": "string" },
14510
+ "org_id": {
14511
+ "type": "string",
14512
+ "format": "uuid"
14513
+ },
14514
+ "org_name": { "type": ["string", "null"] }
12345
14515
  },
12346
14516
  "required": [
12347
- "signup_token",
12348
- "email",
14517
+ "api_key",
14518
+ "key_id",
14519
+ "key_prefix",
14520
+ "access_token",
14521
+ "refresh_token",
14522
+ "token_type",
12349
14523
  "expires_in",
12350
- "resend_after",
12351
- "verification_code_length"
14524
+ "auth_method",
14525
+ "oauth_grant_id",
14526
+ "oauth_client_id",
14527
+ "org_id",
14528
+ "org_name"
12352
14529
  ]
12353
14530
  },
12354
- "sdkName": "startAgentSignup",
12355
- "summary": "Start agent account signup",
12356
- "tag": "Agent",
12357
- "tagCommand": "agent"
14531
+ "sdkName": "pollCliLogin",
14532
+ "summary": "Poll CLI browser login",
14533
+ "tag": "CLI",
14534
+ "tagCommand": "cli"
12358
14535
  },
12359
14536
  {
12360
14537
  "binaryResponse": false,
12361
14538
  "bodyRequired": true,
12362
- "command": "verify-agent-claim",
12363
- "description": "Confirms the verification code emailed by `/agent/claim/start` and\nupgrades the account to the `developer` plan. The org id, API key, and\nmanaged inbox all carry over; the send cap lifts. Authenticated by the\nagent's own API key.\n",
14539
+ "command": "resend-cli-signup-verification",
14540
+ "description": "Sends a new email verification code for a pending CLI signup session.\nThis endpoint does not require an API key.\n",
12364
14541
  "hasJsonBody": true,
12365
14542
  "method": "POST",
12366
- "operationId": "verifyAgentClaim",
12367
- "path": "/agent/claim/verify",
14543
+ "operationId": "resendCliSignupVerification",
14544
+ "path": "/cli/signup/resend",
12368
14545
  "pathParams": [],
12369
14546
  "queryParams": [],
12370
14547
  "requestSchema": {
12371
14548
  "type": "object",
12372
14549
  "additionalProperties": false,
12373
- "properties": { "verification_code": {
14550
+ "properties": { "signup_token": {
12374
14551
  "type": "string",
12375
- "minLength": 1,
12376
- "maxLength": 32,
12377
- "description": "The verification code emailed by the claim start step."
14552
+ "minLength": 1
12378
14553
  } },
12379
- "required": ["verification_code"]
14554
+ "required": ["signup_token"]
12380
14555
  },
12381
14556
  "responseSchema": {
12382
14557
  "type": "object",
12383
14558
  "properties": {
12384
- "org_id": {
12385
- "type": "string",
12386
- "format": "uuid"
12387
- },
12388
- "plan": {
12389
- "type": "string",
12390
- "enum": ["developer"]
12391
- },
12392
14559
  "email": {
12393
14560
  "type": "string",
12394
14561
  "format": "email"
12395
14562
  },
12396
- "limits": {
12397
- "type": "object",
12398
- "description": "Plan-derived quota limits for an account.",
12399
- "properties": {
12400
- "storage_mb": { "type": "number" },
12401
- "send_per_hour": { "type": "number" },
12402
- "send_per_day": { "type": "number" },
12403
- "api_per_minute": { "type": "number" },
12404
- "webhooks_max_global": { "type": ["number", "null"] },
12405
- "webhooks_per_domain": { "type": "boolean" },
12406
- "filters_per_domain": { "type": "boolean" },
12407
- "spam_thresholds_per_domain": { "type": "boolean" }
12408
- },
12409
- "required": [
12410
- "storage_mb",
12411
- "send_per_hour",
12412
- "send_per_day",
12413
- "api_per_minute",
12414
- "webhooks_max_global",
12415
- "webhooks_per_domain",
12416
- "filters_per_domain",
12417
- "spam_thresholds_per_domain"
12418
- ]
14563
+ "expires_in": {
14564
+ "type": "integer",
14565
+ "description": "Seconds until the pending signup expires"
14566
+ },
14567
+ "resend_after": {
14568
+ "type": "integer",
14569
+ "description": "Minimum seconds before requesting another verification email"
14570
+ },
14571
+ "verification_code_length": {
14572
+ "type": "integer",
14573
+ "description": "Number of digits in the emailed verification code"
12419
14574
  }
12420
14575
  },
12421
14576
  "required": [
12422
- "org_id",
12423
- "plan",
12424
14577
  "email",
12425
- "limits"
14578
+ "expires_in",
14579
+ "resend_after",
14580
+ "verification_code_length"
12426
14581
  ]
12427
14582
  },
12428
- "sdkName": "verifyAgentClaim",
12429
- "summary": "Verify an agent account email claim",
12430
- "tag": "Agent",
12431
- "tagCommand": "agent"
14583
+ "sdkName": "resendCliSignupVerification",
14584
+ "summary": "Resend CLI signup verification code",
14585
+ "tag": "CLI",
14586
+ "tagCommand": "cli"
12432
14587
  },
12433
14588
  {
12434
14589
  "binaryResponse": false,
12435
- "bodyRequired": true,
12436
- "command": "verify-agent-signup",
12437
- "description": "Verifies the email code for an agent signup session and creates\nthe account when needed. When the session was started with a\n`signup_code`, the reserved code is redeemed; sessions started\nwithout a code skip the redemption step. An org-scoped OAuth\nsession for CLI authentication is minted and the raw tokens are\nreturned exactly once. For existing users, the optional `org_id`\nselects which accessible workspace should receive the new\nsession (no signup-code redemption is performed for existing\nusers regardless of how the session was started).\n",
14590
+ "bodyRequired": false,
14591
+ "command": "start-cli-login",
14592
+ "description": "Starts a browser-assisted CLI login session. The response includes a\ndevice code for polling and a user code that the user approves in the\nbrowser. This endpoint does not require an API key.\n",
12438
14593
  "hasJsonBody": true,
12439
- "method": "POST",
12440
- "operationId": "verifyAgentSignup",
12441
- "path": "/agent/signup/verify",
14594
+ "method": "POST",
14595
+ "operationId": "startCliLogin",
14596
+ "path": "/cli/login/start",
12442
14597
  "pathParams": [],
12443
14598
  "queryParams": [],
12444
14599
  "requestSchema": {
12445
14600
  "type": "object",
12446
14601
  "additionalProperties": false,
12447
14602
  "properties": {
12448
- "signup_token": {
12449
- "type": "string",
12450
- "minLength": 1
12451
- },
12452
- "verification_code": {
14603
+ "device_name": {
12453
14604
  "type": "string",
12454
14605
  "minLength": 1,
12455
- "maxLength": 32
14606
+ "maxLength": 80,
14607
+ "description": "Human-readable device name shown during browser approval"
12456
14608
  },
12457
- "org_id": {
12458
- "type": "string",
12459
- "format": "uuid",
12460
- "description": "Optional workspace id to target when the verified email already belongs to multiple workspaces"
14609
+ "metadata": {
14610
+ "type": "object",
14611
+ "additionalProperties": true,
14612
+ "description": "Optional client metadata stored with the login session; serialized JSON must be 2048 bytes or fewer"
12461
14613
  }
12462
- },
12463
- "required": ["signup_token", "verification_code"]
14614
+ }
12464
14615
  },
12465
14616
  "responseSchema": {
12466
14617
  "type": "object",
12467
14618
  "properties": {
12468
- "api_key": {
12469
- "type": "string",
12470
- "description": "Legacy alias for access_token. New CLI builds should persist access_token and refresh_token."
12471
- },
12472
- "key_id": {
12473
- "type": "string",
12474
- "format": "uuid",
12475
- "description": "Legacy alias for oauth_grant_id"
12476
- },
12477
- "key_prefix": {
14619
+ "device_code": {
12478
14620
  "type": "string",
12479
- "description": "Legacy display prefix derived from access_token"
14621
+ "description": "Opaque code used by the CLI to poll for approval"
12480
14622
  },
12481
- "access_token": {
14623
+ "user_code": {
12482
14624
  "type": "string",
12483
- "description": "OAuth access token for CLI API authentication"
14625
+ "pattern": "^[BCDFGHJKLMNPQRSTVWXZ]{4}-[BCDFGHJKLMNPQRSTVWXZ]{4}$",
14626
+ "description": "Short code the user confirms in the browser"
12484
14627
  },
12485
- "refresh_token": {
14628
+ "verification_uri": {
12486
14629
  "type": "string",
12487
- "description": "OAuth refresh token used by the CLI to renew access"
14630
+ "description": "Browser URL where the user approves the login"
12488
14631
  },
12489
- "token_type": {
14632
+ "verification_uri_complete": {
12490
14633
  "type": "string",
12491
- "enum": ["Bearer"]
14634
+ "description": "Browser URL with the user code prefilled"
12492
14635
  },
12493
14636
  "expires_in": {
12494
14637
  "type": "integer",
12495
- "description": "Seconds until access_token expires"
12496
- },
12497
- "auth_method": {
12498
- "type": "string",
12499
- "enum": ["oauth"]
12500
- },
12501
- "oauth_grant_id": {
12502
- "type": "string",
12503
- "format": "uuid"
12504
- },
12505
- "oauth_client_id": { "type": "string" },
12506
- "org_id": {
12507
- "type": "string",
12508
- "format": "uuid"
14638
+ "description": "Seconds until the login session expires"
12509
14639
  },
12510
- "org_name": { "type": ["string", "null"] },
12511
- "orgs": {
12512
- "type": "array",
12513
- "items": {
12514
- "type": "object",
12515
- "properties": {
12516
- "id": {
12517
- "type": "string",
12518
- "format": "uuid"
12519
- },
12520
- "name": { "type": ["string", "null"] }
12521
- },
12522
- "required": ["id", "name"]
12523
- },
12524
- "description": "Workspaces available to the verified email. The minted session targets `org_id`."
14640
+ "interval": {
14641
+ "type": "integer",
14642
+ "description": "Minimum seconds between poll requests"
12525
14643
  }
12526
14644
  },
12527
14645
  "required": [
12528
- "api_key",
12529
- "key_id",
12530
- "key_prefix",
12531
- "access_token",
12532
- "refresh_token",
12533
- "token_type",
14646
+ "device_code",
14647
+ "user_code",
14648
+ "verification_uri",
14649
+ "verification_uri_complete",
12534
14650
  "expires_in",
12535
- "auth_method",
12536
- "oauth_grant_id",
12537
- "oauth_client_id",
12538
- "org_id",
12539
- "org_name",
12540
- "orgs"
14651
+ "interval"
12541
14652
  ]
12542
14653
  },
12543
- "sdkName": "verifyAgentSignup",
12544
- "summary": "Verify agent signup and create OAuth tokens",
12545
- "tag": "Agent",
12546
- "tagCommand": "agent"
14654
+ "sdkName": "startCliLogin",
14655
+ "summary": "Start CLI browser login",
14656
+ "tag": "CLI",
14657
+ "tagCommand": "cli"
12547
14658
  },
12548
14659
  {
12549
14660
  "binaryResponse": false,
12550
- "bodyRequired": false,
12551
- "command": "remove-agent-connection",
12552
- "description": "Permanently removes a revoked connection record. Requires an organization\nowner or admin session or OAuth token; organization API keys are denied.\nDisconnect first using DELETE /agent-connections/{address}. An active\nconnection returns 409 connection_not_revoked. Missing or already removed\nrecords return 404. Mail, address notes, domains and external runtimes are\npreserved. The same address can be paired again with a new invitation;\nold credentials and invitations remain invalid.\n",
12553
- "hasJsonBody": false,
14661
+ "bodyRequired": true,
14662
+ "command": "start-cli-signup",
14663
+ "description": "Starts a terminal-native CLI signup. `signup_code` is optional;\nomit it to sign up without one. The API creates a pending signup\nsession, sends an email verification code, and returns an opaque\nsignup token used by the resend and verify steps. This endpoint\ndoes not require an API key.\n",
14664
+ "hasJsonBody": true,
12554
14665
  "method": "POST",
12555
- "operationId": "removeAgentConnection",
12556
- "path": "/agent-connections/{address}/remove",
12557
- "pathParams": [{
12558
- "description": "The email address identifying the revoked connection.",
12559
- "enum": null,
12560
- "name": "address",
12561
- "required": true,
12562
- "type": "string"
12563
- }],
14666
+ "operationId": "startCliSignup",
14667
+ "path": "/cli/signup/start",
14668
+ "pathParams": [],
12564
14669
  "queryParams": [],
12565
- "requestSchema": null,
12566
- "responseSchema": {
14670
+ "requestSchema": {
12567
14671
  "type": "object",
12568
- "required": ["success", "data"],
14672
+ "additionalProperties": false,
12569
14673
  "properties": {
12570
- "success": {
14674
+ "email": {
14675
+ "type": "string",
14676
+ "format": "email",
14677
+ "maxLength": 254
14678
+ },
14679
+ "signup_code": {
14680
+ "type": "string",
14681
+ "minLength": 1,
14682
+ "maxLength": 128,
14683
+ "description": "Optional signup code. Omit if you do not have one."
14684
+ },
14685
+ "terms_accepted": {
12571
14686
  "type": "boolean",
12572
- "const": true
14687
+ "const": true,
14688
+ "description": "Must be true to confirm acceptance of Primitive's Terms of Service and Privacy Policy"
12573
14689
  },
12574
- "data": {
14690
+ "device_name": {
14691
+ "type": "string",
14692
+ "minLength": 1,
14693
+ "maxLength": 80,
14694
+ "description": "Human-readable device name used for the created CLI OAuth grant"
14695
+ },
14696
+ "metadata": {
12575
14697
  "type": "object",
12576
- "required": ["deleted"],
12577
- "properties": { "deleted": {
12578
- "type": "boolean",
12579
- "const": true
12580
- } }
14698
+ "additionalProperties": true,
14699
+ "description": "Optional client metadata stored with the signup session; serialized JSON must be 2048 bytes or fewer"
12581
14700
  }
12582
- }
12583
- },
12584
- "sdkName": "removeAgentConnection",
12585
- "summary": "Remove a revoked agent connection",
12586
- "tag": "Agent Connections",
12587
- "tagCommand": "agent-connections"
12588
- },
12589
- {
12590
- "binaryResponse": false,
12591
- "bodyRequired": false,
12592
- "command": "cli-logout",
12593
- "description": "Revokes the OAuth grant used to authenticate the request. API-key\nauthenticated legacy logout requests succeed without deleting server API\nkeys so old local CLI state can be cleared safely.\n",
12594
- "hasJsonBody": true,
12595
- "method": "POST",
12596
- "operationId": "cliLogout",
12597
- "path": "/cli/logout",
12598
- "pathParams": [],
12599
- "queryParams": [],
12600
- "requestSchema": {
12601
- "type": "object",
12602
- "additionalProperties": false,
12603
- "properties": { "key_id": {
12604
- "type": "string",
12605
- "format": "uuid",
12606
- "description": "Optional id guard; when provided it must match the authenticated OAuth grant id or API key id"
12607
- } }
14701
+ },
14702
+ "required": ["email", "terms_accepted"]
12608
14703
  },
12609
14704
  "responseSchema": {
12610
14705
  "type": "object",
12611
14706
  "properties": {
12612
- "revoked": {
12613
- "type": "boolean",
12614
- "description": "True when an OAuth grant was revoked. False for API-key-authenticated legacy logout, which only clears local CLI state."
12615
- },
12616
- "key_id": {
14707
+ "signup_token": {
12617
14708
  "type": "string",
12618
- "format": "uuid",
12619
- "description": "API key id for API-key-authenticated legacy logout"
14709
+ "description": "Opaque token used to verify or resend the pending CLI signup"
12620
14710
  },
12621
- "oauth_grant_id": {
14711
+ "email": {
12622
14712
  "type": "string",
12623
- "format": "uuid",
12624
- "description": "OAuth grant id revoked by OAuth-authenticated logout"
14713
+ "format": "email"
14714
+ },
14715
+ "expires_in": {
14716
+ "type": "integer",
14717
+ "description": "Seconds until the pending signup expires"
14718
+ },
14719
+ "resend_after": {
14720
+ "type": "integer",
14721
+ "description": "Minimum seconds before requesting another verification email"
14722
+ },
14723
+ "verification_code_length": {
14724
+ "type": "integer",
14725
+ "description": "Number of digits in the emailed verification code"
12625
14726
  }
12626
14727
  },
12627
- "required": ["revoked"]
14728
+ "required": [
14729
+ "signup_token",
14730
+ "email",
14731
+ "expires_in",
14732
+ "resend_after",
14733
+ "verification_code_length"
14734
+ ]
12628
14735
  },
12629
- "sdkName": "cliLogout",
12630
- "summary": "Revoke the current CLI OAuth session",
14736
+ "sdkName": "startCliSignup",
14737
+ "summary": "Start CLI account signup",
12631
14738
  "tag": "CLI",
12632
14739
  "tagCommand": "cli"
12633
14740
  },
12634
14741
  {
12635
14742
  "binaryResponse": false,
12636
14743
  "bodyRequired": true,
12637
- "command": "poll-cli-login",
12638
- "description": "Polls a CLI login session until the browser approval either succeeds,\nis denied, expires, or is polled too quickly. The OAuth token set is\ncreated only after approval and is returned exactly once.\n",
14744
+ "command": "verify-cli-signup",
14745
+ "description": "Verifies the email code for a CLI signup session and creates the\naccount. When the session was started with a `signup_code`, the\nreserved code is redeemed; sessions started without a code skip\nthe redemption step. Either way an org-scoped OAuth CLI session\nis created and the token set is returned exactly once. This\nendpoint does not require an API key.\n",
12639
14746
  "hasJsonBody": true,
12640
14747
  "method": "POST",
12641
- "operationId": "pollCliLogin",
12642
- "path": "/cli/login/poll",
14748
+ "operationId": "verifyCliSignup",
14749
+ "path": "/cli/signup/verify",
12643
14750
  "pathParams": [],
12644
14751
  "queryParams": [],
12645
14752
  "requestSchema": {
12646
14753
  "type": "object",
12647
14754
  "additionalProperties": false,
12648
- "properties": { "device_code": {
12649
- "type": "string",
12650
- "minLength": 1
12651
- } },
12652
- "required": ["device_code"]
14755
+ "properties": {
14756
+ "signup_token": {
14757
+ "type": "string",
14758
+ "minLength": 1
14759
+ },
14760
+ "verification_code": {
14761
+ "type": "string",
14762
+ "minLength": 1,
14763
+ "maxLength": 32
14764
+ },
14765
+ "password": {
14766
+ "type": "string",
14767
+ "minLength": 1,
14768
+ "maxLength": 1024
14769
+ }
14770
+ },
14771
+ "required": ["signup_token", "verification_code"]
12653
14772
  },
12654
14773
  "responseSchema": {
12655
14774
  "type": "object",
@@ -12713,314 +14832,515 @@ const operationManifest = [
12713
14832
  "org_name"
12714
14833
  ]
12715
14834
  },
12716
- "sdkName": "pollCliLogin",
12717
- "summary": "Poll CLI browser login",
14835
+ "sdkName": "verifyCliSignup",
14836
+ "summary": "Verify CLI signup and create OAuth session",
12718
14837
  "tag": "CLI",
12719
14838
  "tagCommand": "cli"
12720
14839
  },
12721
14840
  {
12722
14841
  "binaryResponse": false,
12723
- "bodyRequired": true,
12724
- "command": "resend-cli-signup-verification",
12725
- "description": "Sends a new email verification code for a pending CLI signup session.\nThis endpoint does not require an API key.\n",
12726
- "hasJsonBody": true,
12727
- "method": "POST",
12728
- "operationId": "resendCliSignupVerification",
12729
- "path": "/cli/signup/resend",
12730
- "pathParams": [],
12731
- "queryParams": [],
12732
- "requestSchema": {
14842
+ "bodyRequired": false,
14843
+ "command": "delete-agent-contact",
14844
+ "description": "Organization directory and agent preferences; no profiles, message history or runtime presence. Existing organization credentials use tenant permissions. Connected keys may read the directory, create address-only missing contacts using if_absent:true, and access only their own agent memberships; they cannot edit shared labels or delete directory entries. Function credentials are not granted access. Memberships require an existing local agent connection (including revoked connections) and an existing contact. PUT requires exactly one precondition. if_absent returns an existing row unchanged when supplied fields agree; otherwise 409. notify defaults false. Off-to-on generates a new server timestamp and generation; on-to-on and purpose-only edits preserve them. Disable clears activation metadata. Deletion atomically removes membership eligibility. Receivers must recheck generation/preferences before dispatch and pause admission when their policy cache is older than 30 seconds; explicit reply waits are independent. DELETE requires if_version, returns deleted:false for an absent row, and rejects stale versions of recreated rows. Pages use ascending canonical address, with meta.cursor null on the last page. Lists are live pages, not snapshots.",
14845
+ "hasJsonBody": false,
14846
+ "method": "DELETE",
14847
+ "operationId": "deleteAgentContact",
14848
+ "path": "/agent-contacts/{agent_address}/{contact_address}",
14849
+ "pathParams": [{
14850
+ "description": "agent_address for contacts.",
14851
+ "enum": null,
14852
+ "name": "agent_address",
14853
+ "required": true,
14854
+ "type": "string"
14855
+ }, {
14856
+ "description": "contact_address for contacts.",
14857
+ "enum": null,
14858
+ "name": "contact_address",
14859
+ "required": true,
14860
+ "type": "string"
14861
+ }],
14862
+ "queryParams": [{
14863
+ "description": "if_version for contacts.",
14864
+ "enum": null,
14865
+ "name": "if_version",
14866
+ "required": true,
14867
+ "type": "string"
14868
+ }],
14869
+ "requestSchema": null,
14870
+ "responseSchema": {
12733
14871
  "type": "object",
12734
14872
  "additionalProperties": false,
12735
- "properties": { "signup_token": {
12736
- "type": "string",
12737
- "minLength": 1
12738
- } },
12739
- "required": ["signup_token"]
14873
+ "properties": { "deleted": { "type": "boolean" } },
14874
+ "required": ["deleted"]
12740
14875
  },
14876
+ "sdkName": "deleteAgentContact",
14877
+ "summary": "delete Agent Contact",
14878
+ "tag": "Contacts",
14879
+ "tagCommand": "contacts"
14880
+ },
14881
+ {
14882
+ "binaryResponse": false,
14883
+ "bodyRequired": false,
14884
+ "command": "delete-contact",
14885
+ "description": "Organization directory and agent preferences; no profiles, message history or runtime presence. Existing organization credentials use tenant permissions. Connected keys may read the directory, create address-only missing contacts using if_absent:true, and access only their own agent memberships; they cannot edit shared labels or delete directory entries. Function credentials are not granted access. Memberships require an existing local agent connection (including revoked connections) and an existing contact. PUT requires exactly one precondition. if_absent returns an existing row unchanged when supplied fields agree; otherwise 409. notify defaults false. Off-to-on generates a new server timestamp and generation; on-to-on and purpose-only edits preserve them. Disable clears activation metadata. Deletion atomically removes membership eligibility. Receivers must recheck generation/preferences before dispatch and pause admission when their policy cache is older than 30 seconds; explicit reply waits are independent. DELETE requires if_version, returns deleted:false for an absent row, and rejects stale versions of recreated rows. Pages use ascending canonical address, with meta.cursor null on the last page. Lists are live pages, not snapshots.",
14886
+ "hasJsonBody": false,
14887
+ "method": "DELETE",
14888
+ "operationId": "deleteContact",
14889
+ "path": "/contacts/{address}",
14890
+ "pathParams": [{
14891
+ "description": "address for contacts.",
14892
+ "enum": null,
14893
+ "name": "address",
14894
+ "required": true,
14895
+ "type": "string"
14896
+ }],
14897
+ "queryParams": [{
14898
+ "description": "if_version for contacts.",
14899
+ "enum": null,
14900
+ "name": "if_version",
14901
+ "required": true,
14902
+ "type": "string"
14903
+ }],
14904
+ "requestSchema": null,
12741
14905
  "responseSchema": {
12742
14906
  "type": "object",
12743
- "properties": {
12744
- "email": {
12745
- "type": "string",
12746
- "format": "email"
12747
- },
12748
- "expires_in": {
12749
- "type": "integer",
12750
- "description": "Seconds until the pending signup expires"
12751
- },
12752
- "resend_after": {
12753
- "type": "integer",
12754
- "description": "Minimum seconds before requesting another verification email"
12755
- },
12756
- "verification_code_length": {
12757
- "type": "integer",
12758
- "description": "Number of digits in the emailed verification code"
12759
- }
12760
- },
12761
- "required": [
12762
- "email",
12763
- "expires_in",
12764
- "resend_after",
12765
- "verification_code_length"
12766
- ]
14907
+ "additionalProperties": false,
14908
+ "properties": { "deleted": { "type": "boolean" } },
14909
+ "required": ["deleted"]
12767
14910
  },
12768
- "sdkName": "resendCliSignupVerification",
12769
- "summary": "Resend CLI signup verification code",
12770
- "tag": "CLI",
12771
- "tagCommand": "cli"
14911
+ "sdkName": "deleteContact",
14912
+ "summary": "delete Contact",
14913
+ "tag": "Contacts",
14914
+ "tagCommand": "contacts"
12772
14915
  },
12773
14916
  {
12774
14917
  "binaryResponse": false,
12775
14918
  "bodyRequired": false,
12776
- "command": "start-cli-login",
12777
- "description": "Starts a browser-assisted CLI login session. The response includes a\ndevice code for polling and a user code that the user approves in the\nbrowser. This endpoint does not require an API key.\n",
12778
- "hasJsonBody": true,
12779
- "method": "POST",
12780
- "operationId": "startCliLogin",
12781
- "path": "/cli/login/start",
12782
- "pathParams": [],
14919
+ "command": "get-contact",
14920
+ "description": "Organization directory and agent preferences; no profiles, message history or runtime presence. Existing organization credentials use tenant permissions. Connected keys may read the directory, create address-only missing contacts using if_absent:true, and access only their own agent memberships; they cannot edit shared labels or delete directory entries. Function credentials are not granted access. Memberships require an existing local agent connection (including revoked connections) and an existing contact. PUT requires exactly one precondition. if_absent returns an existing row unchanged when supplied fields agree; otherwise 409. notify defaults false. Off-to-on generates a new server timestamp and generation; on-to-on and purpose-only edits preserve them. Disable clears activation metadata. Deletion atomically removes membership eligibility. Receivers must recheck generation/preferences before dispatch and pause admission when their policy cache is older than 30 seconds; explicit reply waits are independent. DELETE requires if_version, returns deleted:false for an absent row, and rejects stale versions of recreated rows. Pages use ascending canonical address, with meta.cursor null on the last page. Lists are live pages, not snapshots.",
14921
+ "hasJsonBody": false,
14922
+ "method": "GET",
14923
+ "operationId": "getContact",
14924
+ "path": "/contacts/{address}",
14925
+ "pathParams": [{
14926
+ "description": "address for contacts.",
14927
+ "enum": null,
14928
+ "name": "address",
14929
+ "required": true,
14930
+ "type": "string"
14931
+ }],
12783
14932
  "queryParams": [],
12784
- "requestSchema": {
14933
+ "requestSchema": null,
14934
+ "responseSchema": {
12785
14935
  "type": "object",
12786
14936
  "additionalProperties": false,
12787
14937
  "properties": {
12788
- "device_name": {
14938
+ "address": {
12789
14939
  "type": "string",
12790
- "minLength": 1,
12791
- "maxLength": 80,
12792
- "description": "Human-readable device name shown during browser approval"
14940
+ "format": "email",
14941
+ "maxLength": 254,
14942
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
12793
14943
  },
12794
- "metadata": {
12795
- "type": "object",
12796
- "additionalProperties": true,
12797
- "description": "Optional client metadata stored with the login session; serialized JSON must be 2048 bytes or fewer"
12798
- }
12799
- }
12800
- },
12801
- "responseSchema": {
12802
- "type": "object",
12803
- "properties": {
12804
- "device_code": {
12805
- "type": "string",
12806
- "description": "Opaque code used by the CLI to poll for approval"
14944
+ "display_name": {
14945
+ "type": ["string", "null"],
14946
+ "maxLength": 200
12807
14947
  },
12808
- "user_code": {
14948
+ "version": {
12809
14949
  "type": "string",
12810
- "pattern": "^[BCDFGHJKLMNPQRSTVWXZ]{4}-[BCDFGHJKLMNPQRSTVWXZ]{4}$",
12811
- "description": "Short code the user confirms in the browser"
14950
+ "format": "uuid",
14951
+ "description": "Opaque CAS token. Changes on mutations and cannot be reused after deletion/recreation."
12812
14952
  },
12813
- "verification_uri": {
14953
+ "created_at": {
12814
14954
  "type": "string",
12815
- "description": "Browser URL where the user approves the login"
14955
+ "format": "date-time"
12816
14956
  },
12817
- "verification_uri_complete": {
14957
+ "updated_at": {
12818
14958
  "type": "string",
12819
- "description": "Browser URL with the user code prefilled"
14959
+ "format": "date-time"
14960
+ }
14961
+ },
14962
+ "required": [
14963
+ "address",
14964
+ "display_name",
14965
+ "version",
14966
+ "created_at",
14967
+ "updated_at"
14968
+ ]
14969
+ },
14970
+ "sdkName": "getContact",
14971
+ "summary": "get Contact",
14972
+ "tag": "Contacts",
14973
+ "tagCommand": "contacts"
14974
+ },
14975
+ {
14976
+ "binaryResponse": false,
14977
+ "bodyRequired": false,
14978
+ "command": "list-agent-contacts",
14979
+ "description": "Organization directory and agent preferences; no profiles, message history or runtime presence. Existing organization credentials use tenant permissions. Connected keys may read the directory, create address-only missing contacts using if_absent:true, and access only their own agent memberships; they cannot edit shared labels or delete directory entries. Function credentials are not granted access. Memberships require an existing local agent connection (including revoked connections) and an existing contact. PUT requires exactly one precondition. if_absent returns an existing row unchanged when supplied fields agree; otherwise 409. notify defaults false. Off-to-on generates a new server timestamp and generation; on-to-on and purpose-only edits preserve them. Disable clears activation metadata. Deletion atomically removes membership eligibility. Receivers must recheck generation/preferences before dispatch and pause admission when their policy cache is older than 30 seconds; explicit reply waits are independent. DELETE requires if_version, returns deleted:false for an absent row, and rejects stale versions of recreated rows. Pages use ascending canonical address, with meta.cursor null on the last page. Lists are live pages, not snapshots.",
14980
+ "hasJsonBody": false,
14981
+ "method": "GET",
14982
+ "operationId": "listAgentContacts",
14983
+ "path": "/agent-contacts/{agent_address}",
14984
+ "pathParams": [{
14985
+ "description": "agent_address for contacts.",
14986
+ "enum": null,
14987
+ "name": "agent_address",
14988
+ "required": true,
14989
+ "type": "string"
14990
+ }],
14991
+ "queryParams": [{
14992
+ "description": "cursor for contacts.",
14993
+ "enum": null,
14994
+ "name": "cursor",
14995
+ "required": false,
14996
+ "type": "string"
14997
+ }, {
14998
+ "default": 50,
14999
+ "description": "limit for contacts.",
15000
+ "enum": null,
15001
+ "maximum": 100,
15002
+ "minimum": 1,
15003
+ "name": "limit",
15004
+ "required": false,
15005
+ "type": "integer"
15006
+ }],
15007
+ "requestSchema": null,
15008
+ "responseSchema": {
15009
+ "type": "array",
15010
+ "items": {
15011
+ "type": "object",
15012
+ "additionalProperties": false,
15013
+ "properties": {
15014
+ "agent_address": {
15015
+ "type": "string",
15016
+ "format": "email",
15017
+ "maxLength": 254,
15018
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
15019
+ },
15020
+ "contact_address": {
15021
+ "type": "string",
15022
+ "format": "email",
15023
+ "maxLength": 254,
15024
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
15025
+ },
15026
+ "purpose": {
15027
+ "type": ["string", "null"],
15028
+ "maxLength": 2e3
15029
+ },
15030
+ "notify": { "type": "boolean" },
15031
+ "notify_since": {
15032
+ "type": ["string", "null"],
15033
+ "format": "date-time"
15034
+ },
15035
+ "notification_generation": {
15036
+ "type": ["string", "null"],
15037
+ "format": "uuid"
15038
+ },
15039
+ "version": {
15040
+ "type": "string",
15041
+ "format": "uuid",
15042
+ "description": "Opaque CAS token. Changes on mutations and cannot be reused after deletion/recreation."
15043
+ },
15044
+ "created_at": {
15045
+ "type": "string",
15046
+ "format": "date-time"
15047
+ },
15048
+ "updated_at": {
15049
+ "type": "string",
15050
+ "format": "date-time"
15051
+ }
12820
15052
  },
12821
- "expires_in": {
12822
- "type": "integer",
12823
- "description": "Seconds until the login session expires"
15053
+ "required": [
15054
+ "agent_address",
15055
+ "contact_address",
15056
+ "purpose",
15057
+ "notify",
15058
+ "notify_since",
15059
+ "notification_generation",
15060
+ "version",
15061
+ "created_at",
15062
+ "updated_at"
15063
+ ]
15064
+ }
15065
+ },
15066
+ "sdkName": "listAgentContacts",
15067
+ "summary": "list Agent Contacts",
15068
+ "tag": "Contacts",
15069
+ "tagCommand": "contacts"
15070
+ },
15071
+ {
15072
+ "binaryResponse": false,
15073
+ "bodyRequired": false,
15074
+ "command": "list-contacts",
15075
+ "description": "Organization directory and agent preferences; no profiles, message history or runtime presence. Existing organization credentials use tenant permissions. Connected keys may read the directory, create address-only missing contacts using if_absent:true, and access only their own agent memberships; they cannot edit shared labels or delete directory entries. Function credentials are not granted access. Memberships require an existing local agent connection (including revoked connections) and an existing contact. PUT requires exactly one precondition. if_absent returns an existing row unchanged when supplied fields agree; otherwise 409. notify defaults false. Off-to-on generates a new server timestamp and generation; on-to-on and purpose-only edits preserve them. Disable clears activation metadata. Deletion atomically removes membership eligibility. Receivers must recheck generation/preferences before dispatch and pause admission when their policy cache is older than 30 seconds; explicit reply waits are independent. DELETE requires if_version, returns deleted:false for an absent row, and rejects stale versions of recreated rows. Pages use ascending canonical address, with meta.cursor null on the last page. Lists are live pages, not snapshots.",
15076
+ "hasJsonBody": false,
15077
+ "method": "GET",
15078
+ "operationId": "listContacts",
15079
+ "path": "/contacts",
15080
+ "pathParams": [],
15081
+ "queryParams": [{
15082
+ "description": "cursor for contacts.",
15083
+ "enum": null,
15084
+ "name": "cursor",
15085
+ "required": false,
15086
+ "type": "string"
15087
+ }, {
15088
+ "default": 50,
15089
+ "description": "limit for contacts.",
15090
+ "enum": null,
15091
+ "maximum": 100,
15092
+ "minimum": 1,
15093
+ "name": "limit",
15094
+ "required": false,
15095
+ "type": "integer"
15096
+ }],
15097
+ "requestSchema": null,
15098
+ "responseSchema": {
15099
+ "type": "array",
15100
+ "items": {
15101
+ "type": "object",
15102
+ "additionalProperties": false,
15103
+ "properties": {
15104
+ "address": {
15105
+ "type": "string",
15106
+ "format": "email",
15107
+ "maxLength": 254,
15108
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
15109
+ },
15110
+ "display_name": {
15111
+ "type": ["string", "null"],
15112
+ "maxLength": 200
15113
+ },
15114
+ "version": {
15115
+ "type": "string",
15116
+ "format": "uuid",
15117
+ "description": "Opaque CAS token. Changes on mutations and cannot be reused after deletion/recreation."
15118
+ },
15119
+ "created_at": {
15120
+ "type": "string",
15121
+ "format": "date-time"
15122
+ },
15123
+ "updated_at": {
15124
+ "type": "string",
15125
+ "format": "date-time"
15126
+ }
12824
15127
  },
12825
- "interval": {
12826
- "type": "integer",
12827
- "description": "Minimum seconds between poll requests"
12828
- }
12829
- },
12830
- "required": [
12831
- "device_code",
12832
- "user_code",
12833
- "verification_uri",
12834
- "verification_uri_complete",
12835
- "expires_in",
12836
- "interval"
12837
- ]
15128
+ "required": [
15129
+ "address",
15130
+ "display_name",
15131
+ "version",
15132
+ "created_at",
15133
+ "updated_at"
15134
+ ]
15135
+ }
12838
15136
  },
12839
- "sdkName": "startCliLogin",
12840
- "summary": "Start CLI browser login",
12841
- "tag": "CLI",
12842
- "tagCommand": "cli"
15137
+ "sdkName": "listContacts",
15138
+ "summary": "list Contacts",
15139
+ "tag": "Contacts",
15140
+ "tagCommand": "contacts"
12843
15141
  },
12844
15142
  {
12845
15143
  "binaryResponse": false,
12846
15144
  "bodyRequired": true,
12847
- "command": "start-cli-signup",
12848
- "description": "Starts a terminal-native CLI signup. `signup_code` is optional;\nomit it to sign up without one. The API creates a pending signup\nsession, sends an email verification code, and returns an opaque\nsignup token used by the resend and verify steps. This endpoint\ndoes not require an API key.\n",
15145
+ "command": "put-agent-contact",
15146
+ "description": "Organization directory and agent preferences; no profiles, message history or runtime presence. Existing organization credentials use tenant permissions. Connected keys may read the directory, create address-only missing contacts using if_absent:true, and access only their own agent memberships; they cannot edit shared labels or delete directory entries. Function credentials are not granted access. Memberships require an existing local agent connection (including revoked connections) and an existing contact. PUT requires exactly one precondition. if_absent returns an existing row unchanged when supplied fields agree; otherwise 409. notify defaults false. Off-to-on generates a new server timestamp and generation; on-to-on and purpose-only edits preserve them. Disable clears activation metadata. Deletion atomically removes membership eligibility. Receivers must recheck generation/preferences before dispatch and pause admission when their policy cache is older than 30 seconds; explicit reply waits are independent. DELETE requires if_version, returns deleted:false for an absent row, and rejects stale versions of recreated rows. Pages use ascending canonical address, with meta.cursor null on the last page. Lists are live pages, not snapshots.",
12849
15147
  "hasJsonBody": true,
12850
- "method": "POST",
12851
- "operationId": "startCliSignup",
12852
- "path": "/cli/signup/start",
12853
- "pathParams": [],
15148
+ "method": "PUT",
15149
+ "operationId": "putAgentContact",
15150
+ "path": "/agent-contacts/{agent_address}/{contact_address}",
15151
+ "pathParams": [{
15152
+ "description": "agent_address for contacts.",
15153
+ "enum": null,
15154
+ "name": "agent_address",
15155
+ "required": true,
15156
+ "type": "string"
15157
+ }, {
15158
+ "description": "contact_address for contacts.",
15159
+ "enum": null,
15160
+ "name": "contact_address",
15161
+ "required": true,
15162
+ "type": "string"
15163
+ }],
12854
15164
  "queryParams": [],
12855
- "requestSchema": {
15165
+ "requestSchema": { "oneOf": [{
12856
15166
  "type": "object",
12857
15167
  "additionalProperties": false,
12858
15168
  "properties": {
12859
- "email": {
12860
- "type": "string",
12861
- "format": "email",
12862
- "maxLength": 254
12863
- },
12864
- "signup_code": {
12865
- "type": "string",
12866
- "minLength": 1,
12867
- "maxLength": 128,
12868
- "description": "Optional signup code. Omit if you do not have one."
15169
+ "purpose": {
15170
+ "type": ["string", "null"],
15171
+ "maxLength": 2e3
12869
15172
  },
12870
- "terms_accepted": {
15173
+ "notify": { "type": "boolean" },
15174
+ "if_absent": {
12871
15175
  "type": "boolean",
12872
- "const": true,
12873
- "description": "Must be true to confirm acceptance of Primitive's Terms of Service and Privacy Policy"
15176
+ "const": true
15177
+ }
15178
+ },
15179
+ "required": ["if_absent"]
15180
+ }, {
15181
+ "type": "object",
15182
+ "additionalProperties": false,
15183
+ "properties": {
15184
+ "purpose": {
15185
+ "type": ["string", "null"],
15186
+ "maxLength": 2e3
12874
15187
  },
12875
- "device_name": {
15188
+ "notify": { "type": "boolean" },
15189
+ "if_version": {
12876
15190
  "type": "string",
12877
- "minLength": 1,
12878
- "maxLength": 80,
12879
- "description": "Human-readable device name used for the created CLI OAuth grant"
12880
- },
12881
- "metadata": {
12882
- "type": "object",
12883
- "additionalProperties": true,
12884
- "description": "Optional client metadata stored with the signup session; serialized JSON must be 2048 bytes or fewer"
15191
+ "format": "uuid",
15192
+ "description": "Opaque CAS token. Changes on mutations and cannot be reused after deletion/recreation."
12885
15193
  }
12886
15194
  },
12887
- "required": ["email", "terms_accepted"]
12888
- },
15195
+ "required": ["if_version"]
15196
+ }] },
12889
15197
  "responseSchema": {
12890
15198
  "type": "object",
15199
+ "additionalProperties": false,
12891
15200
  "properties": {
12892
- "signup_token": {
15201
+ "agent_address": {
12893
15202
  "type": "string",
12894
- "description": "Opaque token used to verify or resend the pending CLI signup"
15203
+ "format": "email",
15204
+ "maxLength": 254,
15205
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
12895
15206
  },
12896
- "email": {
15207
+ "contact_address": {
12897
15208
  "type": "string",
12898
- "format": "email"
15209
+ "format": "email",
15210
+ "maxLength": 254,
15211
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
12899
15212
  },
12900
- "expires_in": {
12901
- "type": "integer",
12902
- "description": "Seconds until the pending signup expires"
15213
+ "purpose": {
15214
+ "type": ["string", "null"],
15215
+ "maxLength": 2e3
12903
15216
  },
12904
- "resend_after": {
12905
- "type": "integer",
12906
- "description": "Minimum seconds before requesting another verification email"
15217
+ "notify": { "type": "boolean" },
15218
+ "notify_since": {
15219
+ "type": ["string", "null"],
15220
+ "format": "date-time"
12907
15221
  },
12908
- "verification_code_length": {
12909
- "type": "integer",
12910
- "description": "Number of digits in the emailed verification code"
15222
+ "notification_generation": {
15223
+ "type": ["string", "null"],
15224
+ "format": "uuid"
15225
+ },
15226
+ "version": {
15227
+ "type": "string",
15228
+ "format": "uuid",
15229
+ "description": "Opaque CAS token. Changes on mutations and cannot be reused after deletion/recreation."
15230
+ },
15231
+ "created_at": {
15232
+ "type": "string",
15233
+ "format": "date-time"
15234
+ },
15235
+ "updated_at": {
15236
+ "type": "string",
15237
+ "format": "date-time"
12911
15238
  }
12912
15239
  },
12913
15240
  "required": [
12914
- "signup_token",
12915
- "email",
12916
- "expires_in",
12917
- "resend_after",
12918
- "verification_code_length"
15241
+ "agent_address",
15242
+ "contact_address",
15243
+ "purpose",
15244
+ "notify",
15245
+ "notify_since",
15246
+ "notification_generation",
15247
+ "version",
15248
+ "created_at",
15249
+ "updated_at"
12919
15250
  ]
12920
15251
  },
12921
- "sdkName": "startCliSignup",
12922
- "summary": "Start CLI account signup",
12923
- "tag": "CLI",
12924
- "tagCommand": "cli"
15252
+ "sdkName": "putAgentContact",
15253
+ "summary": "put Agent Contact",
15254
+ "tag": "Contacts",
15255
+ "tagCommand": "contacts"
12925
15256
  },
12926
15257
  {
12927
15258
  "binaryResponse": false,
12928
15259
  "bodyRequired": true,
12929
- "command": "verify-cli-signup",
12930
- "description": "Verifies the email code for a CLI signup session and creates the\naccount. When the session was started with a `signup_code`, the\nreserved code is redeemed; sessions started without a code skip\nthe redemption step. Either way an org-scoped OAuth CLI session\nis created and the token set is returned exactly once. This\nendpoint does not require an API key.\n",
15260
+ "command": "put-contact",
15261
+ "description": "Organization directory and agent preferences; no profiles, message history or runtime presence. Existing organization credentials use tenant permissions. Connected keys may read the directory, create address-only missing contacts using if_absent:true, and access only their own agent memberships; they cannot edit shared labels or delete directory entries. Function credentials are not granted access. Memberships require an existing local agent connection (including revoked connections) and an existing contact. PUT requires exactly one precondition. if_absent returns an existing row unchanged when supplied fields agree; otherwise 409. notify defaults false. Off-to-on generates a new server timestamp and generation; on-to-on and purpose-only edits preserve them. Disable clears activation metadata. Deletion atomically removes membership eligibility. Receivers must recheck generation/preferences before dispatch and pause admission when their policy cache is older than 30 seconds; explicit reply waits are independent. DELETE requires if_version, returns deleted:false for an absent row, and rejects stale versions of recreated rows. Pages use ascending canonical address, with meta.cursor null on the last page. Lists are live pages, not snapshots.",
12931
15262
  "hasJsonBody": true,
12932
- "method": "POST",
12933
- "operationId": "verifyCliSignup",
12934
- "path": "/cli/signup/verify",
12935
- "pathParams": [],
15263
+ "method": "PUT",
15264
+ "operationId": "putContact",
15265
+ "path": "/contacts/{address}",
15266
+ "pathParams": [{
15267
+ "description": "address for contacts.",
15268
+ "enum": null,
15269
+ "name": "address",
15270
+ "required": true,
15271
+ "type": "string"
15272
+ }],
12936
15273
  "queryParams": [],
12937
- "requestSchema": {
15274
+ "requestSchema": { "oneOf": [{
12938
15275
  "type": "object",
12939
15276
  "additionalProperties": false,
12940
15277
  "properties": {
12941
- "signup_token": {
12942
- "type": "string",
12943
- "minLength": 1
12944
- },
12945
- "verification_code": {
12946
- "type": "string",
12947
- "minLength": 1,
12948
- "maxLength": 32
15278
+ "display_name": {
15279
+ "type": ["string", "null"],
15280
+ "maxLength": 200
12949
15281
  },
12950
- "password": {
12951
- "type": "string",
12952
- "minLength": 1,
12953
- "maxLength": 1024
15282
+ "if_absent": {
15283
+ "type": "boolean",
15284
+ "const": true
12954
15285
  }
12955
15286
  },
12956
- "required": ["signup_token", "verification_code"]
12957
- },
12958
- "responseSchema": {
15287
+ "required": ["if_absent"]
15288
+ }, {
12959
15289
  "type": "object",
15290
+ "additionalProperties": false,
12960
15291
  "properties": {
12961
- "api_key": {
12962
- "type": "string",
12963
- "description": "Legacy alias for access_token. New CLI builds should persist access_token and refresh_token."
15292
+ "display_name": {
15293
+ "type": ["string", "null"],
15294
+ "maxLength": 200
12964
15295
  },
12965
- "key_id": {
15296
+ "if_version": {
12966
15297
  "type": "string",
12967
15298
  "format": "uuid",
12968
- "description": "Legacy alias for oauth_grant_id"
12969
- },
12970
- "key_prefix": {
12971
- "type": "string",
12972
- "description": "Legacy display prefix derived from access_token"
12973
- },
12974
- "access_token": {
12975
- "type": "string",
12976
- "description": "OAuth access token for CLI API authentication"
12977
- },
12978
- "refresh_token": {
12979
- "type": "string",
12980
- "description": "OAuth refresh token used by the CLI to renew access"
12981
- },
12982
- "token_type": {
15299
+ "description": "Opaque CAS token. Changes on mutations and cannot be reused after deletion/recreation."
15300
+ }
15301
+ },
15302
+ "required": ["if_version"]
15303
+ }] },
15304
+ "responseSchema": {
15305
+ "type": "object",
15306
+ "additionalProperties": false,
15307
+ "properties": {
15308
+ "address": {
12983
15309
  "type": "string",
12984
- "enum": ["Bearer"]
15310
+ "format": "email",
15311
+ "maxLength": 254,
15312
+ "description": "Bare email address; trim and lowercase, preserving dots and plus tags."
12985
15313
  },
12986
- "expires_in": {
12987
- "type": "integer",
12988
- "description": "Seconds until access_token expires"
15314
+ "display_name": {
15315
+ "type": ["string", "null"],
15316
+ "maxLength": 200
12989
15317
  },
12990
- "auth_method": {
15318
+ "version": {
12991
15319
  "type": "string",
12992
- "enum": ["oauth"]
15320
+ "format": "uuid",
15321
+ "description": "Opaque CAS token. Changes on mutations and cannot be reused after deletion/recreation."
12993
15322
  },
12994
- "oauth_grant_id": {
15323
+ "created_at": {
12995
15324
  "type": "string",
12996
- "format": "uuid"
15325
+ "format": "date-time"
12997
15326
  },
12998
- "oauth_client_id": { "type": "string" },
12999
- "org_id": {
15327
+ "updated_at": {
13000
15328
  "type": "string",
13001
- "format": "uuid"
13002
- },
13003
- "org_name": { "type": ["string", "null"] }
15329
+ "format": "date-time"
15330
+ }
13004
15331
  },
13005
15332
  "required": [
13006
- "api_key",
13007
- "key_id",
13008
- "key_prefix",
13009
- "access_token",
13010
- "refresh_token",
13011
- "token_type",
13012
- "expires_in",
13013
- "auth_method",
13014
- "oauth_grant_id",
13015
- "oauth_client_id",
13016
- "org_id",
13017
- "org_name"
15333
+ "address",
15334
+ "display_name",
15335
+ "version",
15336
+ "created_at",
15337
+ "updated_at"
13018
15338
  ]
13019
15339
  },
13020
- "sdkName": "verifyCliSignup",
13021
- "summary": "Verify CLI signup and create OAuth session",
13022
- "tag": "CLI",
13023
- "tagCommand": "cli"
15340
+ "sdkName": "putContact",
15341
+ "summary": "put Contact",
15342
+ "tag": "Contacts",
15343
+ "tagCommand": "contacts"
13024
15344
  },
13025
15345
  {
13026
15346
  "binaryResponse": false,
@@ -15270,7 +17590,7 @@ const operationManifest = [
15270
17590
  "binaryResponse": false,
15271
17591
  "bodyRequired": false,
15272
17592
  "command": "search-emails",
15273
- "description": "Searches inbound emails with structured filters and optional\nfull-text matching across parsed email fields. This endpoint is\noptimized for filtered inbox views and CLI polling workflows:\ncallers that only need new accepted mail can pass\n`sort=received_at_asc`, `snippet=false`, `include_facets=false`,\nand a `date_from` timestamp.\n\n`q`, `subject`, and `body` use the same English full-text index\nas the web inbox search. Structured filters such as `from`, `to`,\n`domain_id`, status, attachment presence, and spam score bounds\nare combined with the text query.\n",
17593
+ "description": "Searches inbound emails with structured filters and optional\nfull-text matching across parsed email fields. This endpoint is\noptimized for filtered inbox views and CLI polling workflows:\ncallers that only need new accepted mail can pass\n`sort=received_at_asc`, `snippet=false`, `include_facets=false`,\nand a `date_from` timestamp.\n\n`q`, `subject`, and `body` use the same English full-text index\nas the web inbox search. Structured filters such as `from`, `to`,\n`domain_id`, status, attachment presence, and spam score bounds\nare combined with the text query.\n\nConnected-agent credentials search only mail received by their own\naddress. This applies to results, totals, facets, and every page;\nsearch filters cannot widen the credential's scope. When\n`reply_to_sent_email_id` is supplied, its parent send must belong\nto the connected address in the same organization. An unavailable\nparent returns 404. Sender filters are not authentication proof;\ninspect the email detail's authentication evidence before trusting it.\n",
15274
17594
  "hasJsonBody": false,
15275
17595
  "method": "GET",
15276
17596
  "operationId": "searchEmails",