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 22 hours 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

  1. Sign in to BA Copilot.
  2. Open Settings β†’ API.
  3. 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

  1. From the same Settings β†’ API page, click Create key.
  2. Give it a memorable name (for example, "production" or "staging-tests").
  3. 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_number is always 1
  • result.version_number is always 1

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.