API reference

Start work in partyline from other software, and hear about it when work finishes. REST over HTTPS, JSON in and out. For the concepts behind these endpoints — keys, signing, the trigger model — read API keys & webhooks.

Two rules worth knowing before you build

  • Webhooks carry ids and links, never your content.Nothing your team writes ends up in a third party's request logs. If a destination needs the detail, it fetches it here with your key.
  • An inbound caller sends data, never instructions. Project, machine and task come from a trigger a human configured; the request supplies only facts about an event.

There is deliberately no write access to runs — nothing outside partyline can approve, merge, or ship.

openapi.jsonOpenAPI 3.1.0 · API v1.0.0 — point a client generator at it.

Authentication

Every request carries a bearer token. Which kind depends on what you are doing:

plt_org_… · plt_trg_…
A team API key from Settings → Integrations. Scoped — a key can only do what it was granted, and a trigger key can start work but read nothing.
plt_cli_…
A personal token from ptln login, for managing webhooks and triggers. Those endpoints are owner/admin only.
authorization: Bearer plt_org_…

Triggers

Let other software start work here. The caller sends data about what happened; the trigger a human configured decides what runs.

post/api/v1/t/{slug}

Fire a trigger

Start work from an outside system. The trigger row holds the project, machine, engine and merge policy; this request supplies only facts about an event, which fill the trigger's {{placeholders}}. Send ref — your own id for the thing that happened — and the same event arriving twice will never start two runs.

Team API keyintake:write

Parameters

slug *string
the trigger's address, chosen when it was created

Body

refstring
your id for this event. Supplying it makes redelivery safe; omitting it means repeat calls start repeat runs.
sourcestring
what system this came from. Shown to the agent as the origin of the quoted input.
*string
any other field is available to the template as {{field}}.

Responses

202
accepted — returns run_id and the trigger's gate
200
duplicate ref — nothing started, returns deduplicated: true
401
missing or invalid key, or the key lacks intake:write
404
no such trigger for this team, or it is switched off
409
the trigger's project or machine is no longer usable
curl -X POST "https://partyline.sh/api/v1/t/sentry-alerts" \ -H "authorization: Bearer plt_org_…" \ -H "content-type: application/json" \ -d '{"ref":"SENTRY-4821","source":"…"}'

Events

What has happened, and the detail behind it. Webhooks carry ids and links only — this is where a destination comes to get the rest.

get/api/v1/events

List events

What has happened in this team, oldest first — the opposite of most listings, because a consumer walking forward with since needs the oldest unseen event, not the newest. Returns next_since to pass back on the following call. This is also how a destination catches up after downtime.

Team API keyevents:read

Parameters

sincestring (ISO 8601)
return events after this time. Use the next_since from the previous page.
kindstring
only this event kind. One of: run.completed · run.failed · run.killed · run.stopped · run.stalled · run.needs_approval · run.started · work_item.accepted · trigger.fired
limitinteger
1–200, default 50

Responses

200
{ events: [...], next_since }
400
since is not a valid timestamp
401
missing or invalid key
403
the key lacks events:read
curl "https://partyline.sh/api/v1/events" \ -H "authorization: Bearer plt_org_…"
get/api/v1/events/{id}

Get one event, with its detail

The payload a webhook deliberately omits. Expands the event's subject — for a run that means status, preset, project, machine, task counts, and each task's branch, summary and PR link. An event belonging to another team returns the same 404 as one that does not exist, so ids cannot be probed.

Team API keyevents:read

Parameters

id *uuid
the event id, from a webhook or the list endpoint

Responses

200
{ event, subject }subject is null when there is nothing to expand
401
missing or invalid key
403
the key lacks events:read
404
no such event for this team
curl "https://partyline.sh/api/v1/events/3f9a2c14-8b7e-4d2a-9f01-6c5b8e2d7a44" \ -H "authorization: Bearer plt_org_…"

Management

Create and manage the webhook endpoints and triggers themselves. Owner/admin only.

get/api/v1/webhooks

List webhook endpoints

Where this team's events are sent. The signing secret is never returned — it is shown once, when the endpoint is created.

User token or session

Responses

200
{ endpoints: [...], kinds: [...] }
401
not signed in
curl "https://partyline.sh/api/v1/webhooks" \ -H "authorization: Bearer plt_cli_…"
post/api/v1/webhooks

Add a webhook endpoint

Returns the signing secret once. The URL must be https and on the public internet — private, loopback and link-local addresses are refused, because our servers make the request and an endpoint pointed inward would reach things it should not.

User token or session

Body

name *string
what it is, e.g. "n8n"
url *string
https, public
kindsstring[]
which events. Empty means everything.

Responses

200
{ endpoint, secret } — the secret is not retrievable again
400
the URL is not https, is a private address, or is not a URL
401
not signed in
403
only owners and admins can add endpoints
curl -X POST "https://partyline.sh/api/v1/webhooks" \ -H "authorization: Bearer plt_cli_…" \ -H "content-type: application/json" \ -d '{"name":"…","url":"https://example.com/issues/4821","kinds":"one"}'
delete/api/v1/webhooks/{id}

Remove a webhook endpoint

Events stop being sent there immediately, and its delivery history goes with it.

User token or session

Parameters

id *uuid
the endpoint

Responses

200
{ ok: true }
401
not signed in
403
only owners and admins can remove endpoints
curl -X DELETE "https://partyline.sh/api/v1/webhooks/3f9a2c14-8b7e-4d2a-9f01-6c5b8e2d7a44" \ -H "authorization: Bearer plt_cli_…"
get/api/v1/triggers

List triggers

The inbound entry points this team has configured, with how many times each has fired.

User token or session

Responses

200
{ triggers: [...] }
401
not signed in
curl "https://partyline.sh/api/v1/triggers" \ -H "authorization: Bearer plt_cli_…"
get/api/v1/triggers/activity

Trigger activity

What every trigger has DONE over a window, bucketed by day and counted by outcome — the series behind the dashboard's Triggers panel. Buckets are dense: every day in the window is present, including the empty ones, so a gap is a zero rather than something to interpolate across. A team with no triggers gets an empty list, not a 404.

User token or session

Parameters

daysinteger
window length, 1–90 (default 14)

Responses

200
{ window_days, triggers: [{ id, name, detail, total, lastAt, inactive, buckets: [{ date, counts }] }] }
400
days is not a whole number between 1 and 90
401
not signed in
403
no team
curl "https://partyline.sh/api/v1/triggers/activity" \ -H "authorization: Bearer plt_cli_…"
get/api/v1/triggers/{id}/events

One trigger's event log

Every inbound call to one trigger, newest first: what it did, the caller's own ref, why nothing ran when nothing did, and the run it started when it started one. {id} accepts the trigger's slug as readily as its uuid, so a caller never has to look up an id to use this. Read-scoped by RLS — a trigger on another team is a 404, not a 403.

User token or session

Parameters

id *string
the trigger's uuid or slug
limitinteger
rows, 1–200 (default 50)

Responses

200
{ trigger, events: [{ id, at, outcome, ref, skipped, run_id, run_status, run_title }], truncated }
400
limit is not a whole number between 1 and 200
401
not signed in
404
no such trigger on your team
curl "https://partyline.sh/api/v1/triggers/3f9a2c14-8b7e-4d2a-9f01-6c5b8e2d7a44/events" \ -H "authorization: Bearer plt_cli_…"
post/api/v1/triggers

Create a trigger

Creating a trigger decides, in advance, what an outside system may make this team's agents do — so it is owner/admin only, and a key with intake:write is issued alongside it and shown once.

User token or session

Body

name *string
what it is
slug *string
the address: lowercase letters, numbers and dashes
project_label *string
which project the work runs in
daemon_id *uuid
which machine runs it
task_template *string
what the agent is asked, with {{placeholders}}
gate"review" | "auto"
`review` (default) queues it for a human; `auto` dispatches immediately
cron_exprstring
ALSO fire on a clock — five-field cron, read in the org's time zone (UTC if unset). Omit for webhook-only. An expression that does not parse is refused, never stored.

Responses

201
{ trigger, key, placeholders }key is shown once
400
a field is missing or the slug is malformed
401
not signed in
403
only owners and admins can create triggers
404
no such project or machine in this team
409
that slug is already taken
curl -X POST "https://partyline.sh/api/v1/triggers" \ -H "authorization: Bearer plt_cli_…" \ -H "content-type: application/json" \ -d '{"name":"…","slug":"sentry-alerts","project_label":"…","daemon_id":"3f9a2c14-8b7e-4d2a-9f01-6c5b8e2d7a44","task_template":"…","gate":"…","cron_expr":"…"}'
patch/api/v1/triggers/{id}

Change a trigger

The pause button, and everything else editable in place — changing a trigger never rotates its key, which is what stops a one-word fix costing a trip back to your CI. Only the fields PRESENT are applied. Turning one off keeps its address reserved; deleting frees it and starts returning errors to anything still calling.

User token or session

Parameters

id *uuid
the trigger

Body

enabledboolean
off keeps the address reserved
cron_exprstring | null
set, change or (null/"") remove the schedule. next_run_at is recomputed in the same patch, so a changed schedule never fires once more on the old one.
schedule_pausedboolean
stop the clock without closing the address. Un-pausing re-arms from now rather than firing everything missed.

Responses

200
{ trigger }
403
only owners and admins
404
no such trigger for this team
curl -X PATCH "https://partyline.sh/api/v1/triggers/3f9a2c14-8b7e-4d2a-9f01-6c5b8e2d7a44" \ -H "authorization: Bearer plt_cli_…" \ -H "content-type: application/json" \ -d '{"enabled":true,"cron_expr":"…","schedule_paused":true}'
delete/api/v1/triggers/{id}

Delete a trigger

Anything still calling that address starts getting errors. Prefer turning it off if you might want the address back.

User token or session

Parameters

id *uuid
the trigger

Responses

200
{ ok: true }
403
only owners and admins
curl -X DELETE "https://partyline.sh/api/v1/triggers/3f9a2c14-8b7e-4d2a-9f01-6c5b8e2d7a44" \ -H "authorization: Bearer plt_cli_…"
get/api/v1/agent-templates

List agent templates

The saved personas a trigger can wake. A template says WHO an agent is and what it is for; the tools, MCP servers and repo it can reach come from the project the trigger targets, never from the template.

User token or session

Responses

200
{ templates: [...] } — drafts and approved, newest first
401
not signed in
curl "https://partyline.sh/api/v1/agent-templates" \ -H "authorization: Bearer plt_cli_…"
post/api/v1/agent-templates

Save an agent template

Records a persona, normally from a finished authoring conversation — pass party_id and that session's document becomes the template. Saved as a draft: a template is the agent, so nothing runs it until a human approves it. A stop_rule is required, because an unattended agent with no instruction to refuse improvises around whatever is missing instead of saying so.

User token or session

Body

party_iduuid
the authoring conversation to take the document from
namestring
how someone picks it from a list. Taken from the document's heading when omitted.
bodystring
the persona, written as instructions to the agent. Taken from the conversation when omitted.
stop_rule *string
when it must refuse rather than guess

Responses

201
{ template } — created as a draft
400
no name, no body, or no stop rule
401
not signed in
409
that name is taken, or the conversation has no document yet
curl -X POST "https://partyline.sh/api/v1/agent-templates" \ -H "authorization: Bearer plt_cli_…" \ -H "content-type: application/json" \ -d '{"party_id":"3f9a2c14-8b7e-4d2a-9f01-6c5b8e2d7a44","name":"…","body":"…","stop_rule":"…"}'
get/api/v1/agent-templates/{id}

Read one agent template

The whole persona, including the body the list deliberately omits — a template is a document, and a list of them is a list, not twenty documents. This is what a trigger will actually run.

User token or session

Parameters

id *uuid
the template's id

Responses

200
{ template } — with body and stop_rule
401
not signed in
404
no such template on your team
curl "https://partyline.sh/api/v1/agent-templates/3f9a2c14-8b7e-4d2a-9f01-6c5b8e2d7a44" \ -H "authorization: Bearer plt_cli_…"
delete/api/v1/agent-templates/{id}

Remove an agent template

Removes the persona outright. Triggers pointing at it are not deleted — they fall back to their own inline task and keep firing, and the response names them so the cost is visible rather than discovered later.

User token or session

Parameters

id *uuid
the template's id

Responses

200
{ ok: true, name, detached: [slug] } — the triggers that just lost it
401
not signed in
403
only owners and admins
404
no such template on your team
curl -X DELETE "https://partyline.sh/api/v1/agent-templates/3f9a2c14-8b7e-4d2a-9f01-6c5b8e2d7a44" \ -H "authorization: Bearer plt_cli_…"

16 endpoints. Anything not listed here — the daemon channel, internal endpoints — is not a public API and changes without notice.