API triggers
API triggers let external systems request documentation updates from Promptless. Connect your CI/CD pipelines, custom automation tools, or any system that can make HTTP requests.
Use cases
Section titled “Use cases”API triggers work well when you want to:
- Integrate Promptless into CI/CD pipelines that run after deployments
- Build custom automation workflows that trigger documentation updates
- Connect external systems (ticketing, project management, custom tools) to Promptless
- Programmatically request documentation updates without using Slack or the dashboard
Set up API triggers
Section titled “Set up API triggers”API triggers are a built-in trigger type that’s always active when an API key exists, no YAML configuration required.
Create an API key
Section titled “Create an API key”You create API keys from your dashboard, and each key authorizes requests for your whole organization. Only organization admins can create or revoke keys.
- Go to Settings > API Access.
- In the Create API key section, enter a Name that says what the key is for. A clear name lets you tell your keys apart when you revoke one later.
- Select Create key.
- Copy the secret from the Copy this key now section as soon as it appears. Promptless shows it only once.
Key management
Section titled “Key management”- No single active key: An organization can hold many API keys at once, each named for the integration that uses it.
- Existing keys carry forward: If your organization already had an API key, it keeps working and appears in the list under the name
Default. - Purpose-based names: Give each key a name that describes what it’s for, such as a CI pipeline, a Zapier connection, or a script. A separate key per integration lets you revoke or rotate that one key without touching the others. Do that after you retire that integration, or after a leak. Each active key needs a distinct name within your organization.
- Independent revocation: Revoking or creating a key affects only that key; the others keep working. This replaces the earlier behavior, where regenerating the key logged out every other caller. Revoking is permanent. A revoked key can’t be restored, so move any caller still using it to another key first.
- At-a-glance status: Each key in the list shows its Name and its key prefix (
sk-pl-..., under the Key column). It also shows when it was Created and when it was Last used. Each key row has its own Revoke control; select it and confirm on that row to remove the key.
Use the API
Section titled “Use the API”For the full request and response schemas, status codes, and an interactive explorer, see the API Reference. The sections below cover these endpoints with examples.
Base URL and versioning
Section titled “Base URL and versioning”All requests use the base URL https://api.gopromptless.ai. The current API version is /v1. New integrations should use POST /v1/triggers; the unversioned POST /triggers is retained for callers built before versioning existed and runs the same operation through the same handler.
Endpoint
Section titled “Endpoint”POST /v1/triggersAuthentication
Section titled “Authentication”Include your API key as a Bearer token in the Authorization header:
Authorization: Bearer sk-pl-your-api-keyThe bearer token determines which organization receives the trigger. There is no need to specify an organization ID in the URL.
Request format
Section titled “Request format”Send a JSON body with your documentation instructions:
{ "instructions": "Update the getting started guide with the new authentication flow", "context": { "ticket_id": "ENG-123", "requested_by": "deploy-bot" }}| Field | Type | Required | Description |
|---|---|---|---|
instructions | string | Yes | What you want Promptless to document. Be specific about which docs to update and what changes to make. |
context | object | No | Additional metadata to include with the request. This appears in trigger history for reference and is passed through to the workflow as additional context. |
Example request
Section titled “Example request”curl -X POST "https://api.gopromptless.ai/v1/triggers" \ -H "Authorization: Bearer sk-pl-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "instructions": "Document the new rate limiting feature added in v2.5", "context": { "release": "v2.5.0", "jira_ticket": "DOC-456" } }'Response
Section titled “Response”A successful request returns a 202 Accepted response:
{ "trigger_event_id": "550e8400-e29b-41d4-a716-446655440000", "deduplicated": false}deduplicated is false for a fresh submission. It is true only when an Idempotency-Key matched an earlier submission, described next.
Idempotent submissions
Section titled “Idempotent submissions”Send an optional Idempotency-Key request header to make a submission safe to retry. The key is a string of up to 255 characters.
- A fresh submission returns a new
trigger_event_idand"deduplicated": false. - A repeat submission that reuses a key already accepted returns the original
trigger_event_idand"deduplicated": true, without creating a second trigger event. - Keys are scoped to your organization. Two organizations can use the same key string without affecting each other.
- A failed submission (any
4xxor5xx) does not consume the key, so you can retry it with the same key. POST /triggersandPOST /v1/triggersshare one idempotency scope, so a key used on one path is recognized on the other.
Reuse a key only for retries of the same request. Reusing one key for two genuinely different requests silently discards the second and points its trigger_event_id at the first request’s task. Derive the key deterministically from the request’s content, for example a hash of instructions plus context. A genuine retry then reuses the same key, while two genuinely different requests get different keys.
curl -X POST "https://api.gopromptless.ai/v1/triggers" \ -H "Authorization: Bearer sk-pl-your-api-key" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: deploy-2026-08-20-v2.5.0" \ -d '{ "instructions": "Document the new rate limiting feature added in v2.5" }'A repeat with the same key returns the original trigger event:
{ "trigger_event_id": "550e8400-e29b-41d4-a716-446655440000", "deduplicated": true}Error responses
Section titled “Error responses”| Status | Error | Description |
|---|---|---|
| 400 | invalid_idempotency_key | The Idempotency-Key header is blank or longer than 255 characters. |
| 400 | invalid_since | The since parameter is not a valid ISO 8601 timestamp. |
| 400 | invalid_filter | event and status were combined in the same request. |
| 401 | authentication_failed | The API key is missing, invalid, or revoked. |
| 409 | org_not_configured | Your organization hasn’t finished setting up Promptless. |
| 409 | no_eligible_doc_collection | No configured doc collection is eligible to receive the request. |
| 422 | Validation error | The request body is invalid. |
| 500 | enqueue_failed | The trigger could not be enqueued for processing. |
| 503 | runtime_store_unavailable | Trigger intake is temporarily unavailable. |
invalid_since and invalid_filter come from the suggestions endpoint. The API enforces no rate limits today, so it never returns a 429.
Read endpoints
Section titled “Read endpoints”The /v1 API also exposes read endpoints for polling integrations. They cover a connection check, the list of your doc collections, a suggestion lifecycle feed, and a finished-task feed. All take the same bearer token and return JSON. Each returns 401 when the key is missing, invalid, or revoked, and 503 when the read store is temporarily unavailable.
Account
Section titled “Account”GET /v1/accountReturns the organization tied to your API key. Use it to test a connection and label the account. org_name is null for an organization created before names were recorded. For the full schema, see the Get account details reference.
curl "https://api.gopromptless.ai/v1/account" \ -H "Authorization: Bearer sk-pl-your-api-key"{ "org_id": "org_2b5f9c1e", "org_name": "Acme, Inc."}Doc collections
Section titled “Doc collections”GET /v1/doc-collectionsLists the doc collections configured for your organization. Each entry carries its id, name, platform, and default_branch. The response is not paged. For the full schema, see the List doc collections reference.
curl "https://api.gopromptless.ai/v1/doc-collections" \ -H "Authorization: Bearer sk-pl-your-api-key"{ "doc_collections": [ { "id": "3f2504e0-4f89-41d3-9a0c-0305e82c3301", "name": "acme/docs", "platform": "github", "default_branch": "main" } ]}Suggestions
Section titled “Suggestions”GET /v1/suggestionsReturns the suggestion lifecycle feed for your organization. That feed covers the suggestions Promptless has created, merged, and closed. For the full schema, see the List suggestions reference.
curl "https://api.gopromptless.ai/v1/suggestions?event=merged&limit=50" \ -H "Authorization: Bearer sk-pl-your-api-key"{ "suggestions": [ { "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "title": "Document the new rate limiting feature", "description": "Add a rate limiting section to the API guide.", "status": "merged", "trigger_event_id": "550e8400-e29b-41d4-a716-446655440000", "url": "https://app.gopromptless.ai/suggestions/7c9e6679-7425-40de-944b-e07fc1f90ae7", "docs_pr_url": "https://github.com/acme/docs/pull/482", "doc_collection_id": "3f2504e0-4f89-41d3-9a0c-0305e82c3301", "doc_collection_name": "acme/docs", "impacted_file_paths": ["docs/api/rate-limits.md"], "impacted_file_count": 1, "created_at": "2026-08-20T14:32:00Z", "merged_at": "2026-08-21T09:15:00Z", "closed_at": null, "closed_without_merge": false, "close_reason": null } ]}Each suggestion carries these fields:
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Stable identifier for the suggestion. Deduplicate a feed on this value. |
title | string | null | Short summary of the documentation change. |
description | string | null | Longer explanation of the change. |
status | string | null | Mirrors the docs pull request’s state: open, draft, merged, or closed. null when the suggestion is drafted but no docs PR has been opened yet; draft when a draft docs PR is open. |
trigger_event_id | string (UUID) | null | Identifier of the task that created the suggestion, the same id POST /v1/triggers returned. null for suggestions created before tasks were recorded. |
url | string | Dashboard link to the suggestion. |
docs_pr_url | string | null | Link to the documentation pull request, or null before a docs PR exists. |
doc_collection_id | string | null | Identifier of the collection the suggestion targets. |
doc_collection_name | string | null | Name of that collection. |
impacted_file_paths | array of string | Paths the suggestion changes. |
impacted_file_count | integer | Number of impacted files. |
created_at | string | ISO 8601 time the suggestion was created. |
merged_at | string | null | ISO 8601 time the docs PR merged, or null. |
closed_at | string | null | ISO 8601 time the suggestion closed, or null. |
closed_without_merge | boolean | true when the suggestion closed without merging. Use this field to distinguish suggestions that shipped from ones that were rejected. |
close_reason | string | null | Supplementary machine-readable label for why the suggestion closed (for example, dashboard_review), or null. This set may expand over time and includes a catch-all such as unknown_legacy, so branch on closed_without_merge rather than hard-coding against close_reason values. |
Filtering
Section titled “Filtering”Narrow the feed with these query parameters, all optional:
| Parameter | Values | Notes |
|---|---|---|
event | created, merged, closed | Selects suggestions by lifecycle event and orders by that event’s timestamp. |
status | open, draft, merged, closed | Selects suggestions by current status. |
query | free text | Matches against the title and description. |
since | ISO 8601 timestamp | Inclusive lower limit on the event timestamp. |
limit | 1–100 | Number of suggestions to return. Defaults to 50. |
offset | 0 or greater | Number of suggestions to skip. Defaults to 0. |
event and status cannot be combined in the same request; sending both returns 400 invalid_filter. Use event to poll a lifecycle feed and status to search by current state.
Polling
Section titled “Polling”Results come back newest-first, ordered by the selected event’s timestamp in descending order. Build an incremental loop against that order:
- Track the newest event timestamp you have seen and pass it as
sinceon the next poll, rather than a wall-clock “now”. Becausesinceis an inclusive lower limit, this returns an event that committed a moment after your last poll instead of skipping it. - Since
sinceis inclusive, a row whose event timestamp equalssinceis returned again. Deduplicate results byid. - Omitting
sincereturns from the start of the available history. - Page through a large result set with
limitandoffset. A response that returns fewer thanlimitsuggestions means you have reached the end of the current set. eventaccepts only one value per request and cannot be combined withstatus. The full lifecycle covers created, merged, and closed-without-merge. Track it by running a separate polling loop with its ownsincecursor for eacheventvalue.- There is no cursor token; polling is driven entirely by
since,limit, andoffset.
A minimal incremental loop for one event value:
# Run one loop per event value (created, merged, closed).since = null # omitted on the first poll -> full historyseen = set() # ids already handled, for dedup
repeat on an interval: offset = 0 newest = since while true: page = GET /v1/suggestions?event=merged&limit=100&offset=offset (add "&since=<since>" when since is set) for s in page.suggestions: # newest-first if s.id not in seen: process(s) seen.add(s.id) newest = max(newest, s.merged_at) if len(page.suggestions) < 100: # fewer than limit -> end of set break offset = offset + 100 since = newest # inclusive lower bound for next pollFinished tasks
Section titled “Finished tasks”GET /v1/triggersLists your organization’s finished tasks, newest completion first, including tasks that made no documentation change. For the full schema, see the List finished tasks reference.
Only finished tasks appear in this feed. A task still running has no outcome yet, so follow one in flight with GET /v1/triggers/{trigger_event_id}. The feed omits Promptless’s internal re-runs. If Promptless reopens a finished task to rework it, the task appears again when it finishes, with a new finished_at and possibly a different outcome.
curl "https://api.gopromptless.ai/v1/triggers?source=api&limit=50" \ -H "Authorization: Bearer sk-pl-your-api-key"{ "tasks": [ { "trigger_event_id": "d3aa7c40-5ec4-48c6-8d10-81c2ee042690", "source": "api", "request": "API: Update the retry documentation", "outcome": "no_change_needed", "resolution": "The retry guide already documents the new backoff, so no change was needed.", "submitted_at": "2026-08-24T10:00:00+00:00", "finished_at": "2026-08-24T10:18:00+00:00" } ]}Each task carries these fields:
| Field | Type | Description |
|---|---|---|
trigger_event_id | string (UUID) | The task’s id, the same id POST /v1/triggers returned. |
source | string | The task’s origin, such as api. |
request | string | null | A one-line summary of the submitted request. |
outcome | string | null | How the task ended. See Outcomes. |
resolution | string | null | A note that explains how the task ended. |
submitted_at | string | ISO 8601 time the task was submitted. |
finished_at | string | ISO 8601 time the task finished. |
Outcomes
Section titled “Outcomes”outcome is one of these values:
| Value | Meaning |
|---|---|
suggestions_created | The task produced at least one suggestion. |
no_change_needed | The task produced none because the docs already cover the change. |
needs_input | The task produced none and ended on a question the customer must answer. A later answer starts a new task. |
failed | Promptless could not finish the work. |
outcome is null for tasks that finished before Promptless added the field and for tasks from sources other than the API or MCP. Promptless may add values later, so handle values not listed here.
This feed omits the suggestions a task produced. Each suggestion in the Suggestions feed carries trigger_event_id, so join the two feeds on that field.
Filtering
Section titled “Filtering”Narrow the feed with these query parameters, all optional:
| Parameter | Values | Notes |
|---|---|---|
since | ISO 8601 timestamp | Inclusive lower limit on the completion timestamp. A trailing Z is accepted, and a value without a timezone is read as UTC. |
source | trigger source | Exact source to return, such as api. Omit to return every source. |
limit | 1–100 | Number of tasks to return. Defaults to 50. |
offset | 0 or greater | Number of tasks to skip. Defaults to 0. |
An invalid since returns 400 invalid_since.
Polling
Section titled “Polling”Poll this feed the way you poll the Suggestions feed, ordered by when each task completed:
- Results come back newest-completion-first, ordered by completion timestamp and then by
id. - Track the newest
finished_atyou have seen and pass it assinceon the next poll. Becausesinceis an inclusive lower limit, a task that finishes a moment after your last poll comes back on the next one. - Since
sinceis inclusive, a task whosefinished_atequalssincecomes back again. Deduplicate bytrigger_event_idandfinished_attogether. A reopened task that finishes again keeps itstrigger_event_idbut carries a newfinished_at, so deduplicating on the pair keeps its latest outcome instead of discarding it. - Page through a large result set with
limitandoffset. A response with fewer thanlimittasks means you have reached the end of the current set. - A task finishing mid-poll moves rows to a higher offset, so a walk by
offsetreturns it on a later page.
Task status and messages
Section titled “Task status and messages”After you submit a task, follow its progress and continue the conversation on the same trigger event. Both operations use the same Authorization: Bearer sk-pl-... token. The conversation is available for tasks submitted over the API or MCP and for tasks created through MCP request_changes.
Read task status
Section titled “Read task status”GET /v1/triggers/{trigger_event_id}trigger_event_id is the id returned by POST /v1/triggers. A successful request returns 200 OK with the task’s current state:
| Field | Type | Description |
|---|---|---|
trigger_event_id | string (UUID) | The task’s id. |
status | string | The task’s current status. The status string can change over time, so branch on finished to detect completion. |
finished | boolean | true when the task has completed or been skipped. |
submitted_at | string | ISO 8601 time the task was submitted. |
source | string | The task’s origin. |
request | string | null | A one-line summary of the submitted request. |
resolution | string | null | A note that explains how the task ended. Read it after finished is true. |
outcome | string | null | How the task ended. See Outcomes for the values. null until the task finishes, and for tasks from sources other than the API or MCP. |
suggestions | array | A per-task summary of each suggestion this task produced, with the fields listed below. This is a lighter shape than the Suggestions feed. |
status_guidance | string | Human- or LLM-readable advice for polling the task and interpreting its outcome. Key on finished, a boolean, to detect completion. Do not parse status_guidance in code. |
messages | array | The persisted conversation, latest 100, oldest-first. Reading the conversation never consumes it. |
Each entry in suggestions carries these fields:
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Stable identifier for the suggestion. |
title | string | null | Short summary of the documentation change. |
description | string | null | Longer explanation of the change. |
status | string | null | The docs pull request’s state: open, draft, merged, or closed. null before a docs PR exists. |
doc_collection_id | string (UUID) | null | Identifier of the collection the suggestion targets. |
docs_pr_url | string | null | Link to the documentation pull request, or null before a docs PR exists. |
branch_name | string | The git branch in the documentation repository. |
labels | array of string | Labels on the suggestion. |
assignees | array of string | Assignees on the suggestion. |
created_at | string | ISO 8601 time the suggestion was created. |
The suggestions array lists the suggestions this task produced with their current status and docs pull request link. To track a suggestion’s full lifecycle, including whether it merged or closed without merging, use the Suggestions feed and its closed_without_merge field.
Each entry in messages has the TaskMessage shape below. The send endpoint returns the same shape.
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Stable, immutable identifier. Deduplicate on it. |
trigger_event_id | string (UUID) | The task this message belongs to. |
sequence | integer | The message’s order within the task. |
author | object | type is promptless or customer. name is the customer’s email when available, otherwise null. |
created_at | string | ISO 8601 time the message was created. |
message | string | The message text. |
Reading status works for any task source. Promptless populates the messages array for tasks submitted over the API or MCP and for tasks created through MCP request_changes. A task you submit over the API is eligible for a conversation. An empty messages array on your own API task means the task has no messages yet. Tasks from other sources, such as Slack, a GitHub pull request, or the dashboard, carry no conversation, so their messages array is empty.
curl "https://api.gopromptless.ai/v1/triggers/550e8400-e29b-41d4-a716-446655440000" \ -H "Authorization: Bearer sk-pl-your-api-key"{ "trigger_event_id": "550e8400-e29b-41d4-a716-446655440000", "status": "in_progress", "finished": false, "submitted_at": "2026-08-20T14:32:00Z", "source": "api", "request": "Document the new rate limiting feature added in v2.5", "resolution": null, "outcome": null, "suggestions": [ { "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "title": "Document the new rate limiting feature", "description": "Add a rate limiting section to the API guide.", "status": "open", "doc_collection_id": "3f2504e0-4f89-41d3-9a0c-0305e82c3301", "docs_pr_url": "https://github.com/acme/docs/pull/482", "branch_name": "promptless/document-rate-limits", "labels": ["documentation"], "assignees": ["deploy-bot@acme.com"], "created_at": "2026-08-20T14:35:00Z" } ], "status_guidance": "Keep polling until finished is true.", "messages": [ { "id": "b1e2c3d4-5678-90ab-cdef-1234567890ab", "trigger_event_id": "550e8400-e29b-41d4-a716-446655440000", "sequence": 1, "author": { "type": "customer", "name": "deploy-bot@acme.com" }, "created_at": "2026-08-20T14:32:05Z", "message": "Document the new rate limiting feature added in v2.5" } ]}Poll this endpoint on an interval until finished is true. Back off and retry on a 503. When you read the conversation, deduplicate messages by their stable id so a repeated poll does not reprocess one.
For the full schema, see the Get task status reference.
Send a task message
Section titled “Send a task message”POST /v1/triggers/{trigger_event_id}/messagesPromptless appends the message to the original task named by trigger_event_id. The reply stays on that task’s conversation, and no new task is created.
| Field | Type | Required | Description |
|---|---|---|---|
message | string | Yes | The instruction to add. Non-empty after trimming, up to 20,000 characters. |
Send an optional Idempotency-Key request header, up to 255 characters, to retry safely. The same key with the same text returns the original stored message with deduplicated set to true. The same key with different text returns 409 idempotency_conflict. The key is scoped to the task and your API key, so the same key value used on a different task is independent.
A new message returns 201 Created with body { "message": <TaskMessage>, "deduplicated": false }. An idempotent replay returns 200 OK with body { "message": <original TaskMessage>, "deduplicated": true }.
curl -X POST "https://api.gopromptless.ai/v1/triggers/550e8400-e29b-41d4-a716-446655440000/messages" \ -H "Authorization: Bearer sk-pl-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "message": "Also document the 429 rate-limit response for this endpoint." }'{ "message": { "id": "c2f3d4e5-6789-01bc-def2-34567890abcd", "trigger_event_id": "550e8400-e29b-41d4-a716-446655440000", "sequence": 2, "author": { "type": "customer", "name": "deploy-bot@acme.com" }, "created_at": "2026-08-20T14:40:00Z", "message": "Also document the 429 rate-limit response for this endpoint." }, "deduplicated": false}A retry that sends the same text with the same Idempotency-Key returns the stored message and sets deduplicated to true:
{ "message": { "id": "c2f3d4e5-6789-01bc-def2-34567890abcd", "trigger_event_id": "550e8400-e29b-41d4-a716-446655440000", "sequence": 2, "author": { "type": "customer", "name": "deploy-bot@acme.com" }, "created_at": "2026-08-20T14:40:00Z", "message": "Also document the 429 rate-limit response for this endpoint." }, "deduplicated": true}For the full schema, see the Send a task message reference.
Task error responses
Section titled “Task error responses”Both operations return 401 authentication_failed when the key is missing, invalid, or revoked. Both return 404 task_not_found for an unknown task or one in another organization. Both return 503 runtime_store_unavailable when the read store is temporarily unavailable.
| Status | Error | Description | Applies to |
|---|---|---|---|
| 404 | task_not_found | Unknown task, or a task in another organization. | both |
| 422 | Validation error | The trigger_event_id in the path isn’t a valid UUID, or the request body is malformed. | both |
| 409 | task_finished | The task has finished and stops accepting messages. A task that finished by reporting a blocker or a clarification request has still finished. To continue the work, submit a new trigger with POST /v1/triggers. | send |
| 400 | unsupported_task_source | Task messages are available for tasks submitted over the API or MCP and for MCP request_changes tasks. Read the task’s status with GET /v1/triggers/{trigger_event_id} (status is available for every source), or submit the work as a new API trigger to converse. | send |
| 400 | invalid_message | The message is empty or longer than 20,000 characters after trimming, or an Idempotency-Key that is blank or longer than 255 characters. | send |
| 409 | idempotency_conflict | The same Idempotency-Key was reused with different text. | send |
| 401 | authentication_failed | The API key is missing, invalid, or revoked. | both |
| 503 | runtime_store_unavailable | Task intake or lookup is temporarily unavailable. | both |
View API triggers
Section titled “View API triggers”API-triggered events appear in your dashboard with an API Task pill. A task that reaches the API through a Promptless-built client carries that client’s own pill. A task submitted through the Promptless Zapier app shows a Zapier Task pill. A task submitted from your editor over MCP shows an MCP Task pill.
Trigger history
Section titled “Trigger history”View all API triggers on the Triggers page. API triggers show the submitted instructions and any context you included in the request.
Suggestion history
Section titled “Suggestion history”Set the Trigger source filter to “API” on the Suggestions list to see documentation suggestions that came from API requests.
How it works
Section titled “How it works”When you submit an API request:
- Validation: Promptless validates your API key and request format.
- Routing: The request is routed to your configured doc collections.
- Processing: Promptless analyzes your instructions along with configured context sources.
- Suggestion Creation: If documentation updates are needed, Promptless creates suggestions.