Partner
A partner is a business that sells Cowliss inside its own product, one organization per customer, and the customer never signs in. These routes take a partner key (Authorization: Bearer cpk_...), issued by arrangement, and accept nothing else; every other route refuses a partner key. create returns an org:admin pipeline key once, and from then on the partner talks to that organization with it on the ordinary routes (apps, sources, keys, sending domains, automations, broadcasts, the billing summary): in an organization a partner created, that key acts as org:admin everywhere a signed-in admin would, because nobody can sign in. An organization the partner did not create is a 404 on every route here. Plan, overage, suspension and purge are the partner’s to call in whatever order its own lifecycle needs; Cowliss enforces none of that ordering. See the Partners guide.
POST /v1/partner/orgs
Section titled “POST /v1/partner/orgs”Create an organization for a customer
Creates the organization with nobody in it, records the partner on it, and mints one org:admin pipeline key for it. The key is in this response and nowhere else; store it. A second create with the same externalId is allowed: the partner decides what one customer maps to.
Auth: Session token.
Request body
{ "data": { "name": "string", "slug": "string", "externalId": "string" }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.name |
string | yes | length 1–100 |
data.slug |
string | no | length 1–100 |
data.externalId |
string | no | length 1–200 |
Response 201
{ "data": { "orgId": "string", "slug": "string", "pipelineKey": "string" }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.orgId |
string | yes | |
data.slug |
string | yes | |
data.pipelineKey |
string | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
GET /v1/partner/orgs/{id}
Section titled “GET /v1/partner/orgs/{id}”Read an organization the partner created
Auth: Session token.
Response 200
{ "data": { "orgId": "string", "slug": "string", "name": "string", "externalId": "string", "plan": "free", "overageAllowed": true, "status": "active", "createdAt": "2026-01-15T09:30:00.000Z" }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.orgId |
string | yes | |
data.slug |
string | null | yes | |
data.name |
string | null | yes | |
data.externalId |
string | null | yes | |
data.plan |
“free” | “plus” | yes | |
data.overageAllowed |
boolean | yes | |
data.status |
“active” | “suspended” | yes | |
data.createdAt |
string (date-time) | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
PUT /v1/partner/orgs/{id}/plan
Section titled “PUT /v1/partner/orgs/{id}/plan”Set the organization’s plan and whether overage is allowed
The plan set here wins over any subscription. With overageAllowed, usage past the included amount is counted (see overageMicros on the billing summary) and never refused, for the partner to bill through its own channel. Included usage, app and sending-domain limits per plan are unchanged.
Auth: Session token.
Request body
{ "data": { "plan": "free", "overageAllowed": true }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.plan |
“free” | “plus” | yes | |
data.overageAllowed |
boolean | yes |
Response 200
{ "data": { "orgId": "string", "slug": "string", "name": "string", "externalId": "string", "plan": "free", "overageAllowed": true, "status": "active", "createdAt": "2026-01-15T09:30:00.000Z" }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.orgId |
string | yes | |
data.slug |
string | null | yes | |
data.name |
string | null | yes | |
data.externalId |
string | null | yes | |
data.plan |
“free” | “plus” | yes | |
data.overageAllowed |
boolean | yes | |
data.status |
“active” | “suspended” | yes | |
data.createdAt |
string (date-time) | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
POST /v1/partner/orgs/{id}/suspend
Section titled “POST /v1/partner/orgs/{id}/suspend”Suspend the organization
Ingestion refuses, no automation starts a run (runs in flight finish with their sends skipped), nothing is sent, and no broadcast can be scheduled. Data is kept. Already suspended is a no-op.
Auth: Session token.
Response 200
{ "data": { "orgId": "string", "slug": "string", "name": "string", "externalId": "string", "plan": "free", "overageAllowed": true, "status": "active", "createdAt": "2026-01-15T09:30:00.000Z" }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.orgId |
string | yes | |
data.slug |
string | null | yes | |
data.name |
string | null | yes | |
data.externalId |
string | null | yes | |
data.plan |
“free” | “plus” | yes | |
data.overageAllowed |
boolean | yes | |
data.status |
“active” | “suspended” | yes | |
data.createdAt |
string (date-time) | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
POST /v1/partner/orgs/{id}/restore
Section titled “POST /v1/partner/orgs/{id}/restore”Restore a suspended organization
Clears the suspension; automations that are on trigger again. Not suspended is a no-op.
Auth: Session token.
Response 200
{ "data": { "orgId": "string", "slug": "string", "name": "string", "externalId": "string", "plan": "free", "overageAllowed": true, "status": "active", "createdAt": "2026-01-15T09:30:00.000Z" }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.orgId |
string | yes | |
data.slug |
string | null | yes | |
data.name |
string | null | yes | |
data.externalId |
string | null | yes | |
data.plan |
“free” | “plus” | yes | |
data.overageAllowed |
boolean | yes | |
data.status |
“active” | “suspended” | yes | |
data.createdAt |
string (date-time) | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
DELETE /v1/partner/orgs/{id}
Section titled “DELETE /v1/partner/orgs/{id}”Delete the organization and everything in it
Starts the purge and returns at once: every run is cancelled, every row and event removed, sending domains released, keys revoked, and the organization itself deleted last. Irreversible. A second call while it runs, or after it finished, answers the same.
Auth: Session token.
Response 202
{ "data": { "workflowId": "string" }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.workflowId |
string | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.