Use BA Copilot with the Process Maps API
Generate and refine BPMN process maps programmatically with the BA Copilot REST API.
Written By Jack Finnegan
Last updated About 2 months ago
The Process Maps API
The Process Maps API lets you generate and refine BPMN process maps programmatically. Send a text prompt to a single endpoint and get back BPMN 2.0 XML you can render, store, or feed into your own tools.
Try the public mock demo at ba-copilot.com/api-access first to see what the request and response look like with no signup required.
Enable API access
- Sign in to BA Copilot.
- Open Settings β API.
- Enable the Process Maps API Labs feature. (It is currently a Labs feature, so the contract may evolve before general availability.)
Create an API key
- From the same Settings β API page, click Create key.
- Give it a memorable name (for example, "production" or "staging-tests").
- The full key is shown once. Copy it and store it securely β we only keep a hash, so we cannot recover it for you. Keys are prefixed
bac_sk_.
Authenticate every request with a bearer token:
Authorization: Bearer bac_sk_...The Test Console
Settings β API also includes a built-in Test Console. You can:
- Choose Initial or Refinement mode.
- Edit the JSON request body inline.
- Send a mock request (instant, no API key, no quota used) or a live request (uses 1 generation from your quota).
- Inspect the response as a rendered BPMN preview, raw JSON, or BPMN XML.
- Promote any returned diagram into a real Process Map in your workspace.
Two modes: initial and refinement
Initial β create a new process map
Send a text prompt and the API returns three distinct BPMN options so you (or your end user) can pick the best starting point.
POST /api/v1/process-maps/generations Authorization: Bearer bac_sk_... Content-Type: application/json { "mode": "initial", "prompt": "Map the customer support ticket triage process from intake to resolution.", "language": "english" } Successful response (200 OK):
{ "id": "33333333-3333-4333-8333-333333333333", "status": "succeeded", "mode": "initial", "request_id": "req_01HZX2KQ8Q3W4Y5Z6A7B8C9D0E", "options": [ { "option_number": 1, "versions": [{ "version_number": 1, "bpmn_xml": "..." }] }, { "option_number": 2, "versions": [{ "version_number": 1, "bpmn_xml": "..." }] }, { "option_number": 3, "versions": [{ "version_number": 1, "bpmn_xml": "..." }] } ] } Refinement β evolve an existing process map
Send a follow-up prompt to refine an existing diagram. The API returns one updated result. Reference a stored generation by id (recommended), or pass inline BPMN XML in source.bpmn_xml.
POST /api/v1/process-maps/generations Authorization: Bearer bac_sk_... Content-Type: application/json { "mode": "refinement", "prompt": "Add an escalation path for high-severity tickets.", "source": { "generation_id": "33333333-3333-4333-8333-333333333333", "option_number": 2, "version_number": 1 } } Successful response:
{ "id": "44444444-4444-4444-8444-444444444444", "status": "succeeded", "mode": "refinement", "request_id": "req_01HZX2KQ8Q3W4Y5Z6A7B8C9D0E", "result": { "option_number": 1, "version_number": 2, "bpmn_xml": "...", "source": { "generation_id": "33333333-3333-4333-8333-333333333333", "option_number": 2, "version_number": 1 } } } Sync vs async
By default the request waits for completion (?wait=true) and returns the BPMN inline. If a generation runs longer than the request window, you get a 202 Accepted with status: "running" and a links.self URL to poll.
Set ?wait=false to start the job in the background and immediately receive a 202 with the self link β useful when you do not want to hold a long-lived HTTP connection.
{ "id": "22222222-2222-4222-8222-222222222222", "status": "running", "request_id": "req_...", "links": { "self": "/api/v1/process-maps/generations/22222222-2222-4222-8222-222222222222" } } Background jobs are best-effort. If a generation does not finish within the time limit it transitions to timed_out. The full timeout is 10 minutes.
Idempotency
Pass an Idempotency-Key header (any string up to 256 characters, typically a UUID) to make retries safe. If you replay a request with the same key and body within 30 days, the API returns the original response without starting a new generation.
Idempotency-Key: 9d4e0a36-8a16-4b7b-9c2c-8b1b0e9a7c54 List, retrieve, and delete generations
Generations are stored against your account so you can fetch them later.
GET /api/v1/process-maps/generations # cursor-paginated list GET /api/v1/process-maps/generations/{id} # retrieve one To delete a stored generation, use the dashboard: open Settings β API β History, select the item, and click Delete stored result.
Limits and quotas
- Prompts are capped at 50,000 characters.
- Generations time out after 10 minutes (status transitions to timed_out).
- Trial users get a 50-generation API quota; subscribers consume their plan's generation quota.
- Idempotency keys are retained for 30 days.
Error format
All non-2xx responses share a consistent error envelope:
{ "error": { "code": "invalid_request", "message": "prompt must be 50,000 characters or fewer.", "request_id": "req_...", "details": { "field": "prompt" } } } Always log request_id β it is the fastest way for support to trace what happened.
OpenAPI spec
The full machine-readable contract is at https://ba-copilot.com/api/v1/openapi.json. Use it to generate clients, run mock servers, or import into Postman.
Promoting API generations to Process Maps
Generations created through the API show up in Settings β API β History. From there you can promote any returned option/version into a real Process Map in any of your workspaces β useful for testing prompts via the API and then continuing to refine them with the chat UI.
Chaining refinements
Each call to POST /api/v1/process-maps/generations creates a new generation with a brand-new id. Refinements are no exception β a refinement is not "version 2 of the original", it is a new generation that happens to reference the source generation in its source.generation_id field.
That means in every refinement response:
result.option_numberis always1result.version_numberis always1
Why? Each generation is its own root. The API deliberately does not maintain a global "this is the 4th refinement" counter β that would force the API to make domain decisions you would usually want to make yourself (does branching reset the counter? does discarding a refinement renumber the chain?).
If you want to render a "draft 1 β draft 2 β draft 3" timeline in your product, walk the source.generation_id links yourself and assign your own ordinal. A typical client-side helper:
// Walk source.generation_id back to the root and number the chain. async function buildChain(generationId, depth = 0) { const gen = await fetch(`/api/v1/process-maps/generations/${generationId}`) .then((res) => res.json()); const sourceId = gen.result?.source?.generation_id; if (!sourceId) return [{ id: gen.id, label: `v${depth + 1}`, gen }]; const chain = await buildChain(sourceId, depth + 1); return [...chain, { id: gen.id, label: `v${depth + 1}`, gen }]; }Two refinements off the same source naturally form a fork β both will return (1, 1) in their own generations. If your product needs to handle forks, treat the chain as a tree, not a list.
Why initial generations also use version_number: 1
For symmetry. option_number ranges 1β3 for initial generations and is always 1 for refinements. version_number is always 1. The field encodes "which option/version within this single generation" β not evolution depth. Keeping it constant keeps the semantics clean.
Need help?
This is a Labs feature, so the surface area is still evolving. If you hit something unexpected, email support@ba-copilot.com with the request_id from the response and we will dig in.