# Flowyn documentation > Flowyn is API-first infrastructure for applications and AI agents: documents (Process), durable HTTP jobs and schedules (Execute), events and webhooks (Connect), monitors, heartbeats, and issues (Observe), keys and flags (Configure), plus runs, traces, model usage, and human approvals for agents customers deploy themselves. Flowyn does not host or execute agents. Every page of https://flowyn.dev/docs as Markdown. Summary: https://flowyn.dev/llms.txt. Authoritative API contract: https://api.flowyn.dev/openapi/v1.yaml. ## Getting started ### Quickstart: from account to first operation URL: https://flowyn.dev/docs/getting-started/quickstart Create a Flowyn account and an API key, send your first request, keep the id it returns, and read the result back. This page takes you from zero to one completed Flowyn operation in the test environment. The same path works for documents, events, jobs, schedules, monitors, flags, and agent runs. #### 1. Create an account and workspace Sign up at [the dashboard](https://app.flowyn.dev/signup). Every workspace has a **test** environment and a **live** environment. Resources in one are never visible in the other. #### 2. Create an API key In the dashboard, open **API keys** and create a key for the test environment. It starts with `flw_test_` and is shown once. Store it as `FLOWYN_API_KEY` in your server environment. Never ship it in client code. bash: ```bash export FLOWYN_API_KEY=flw_test_•••• ``` #### 3. Create a delivery endpoint Create an HTTPS endpoint subscribed to the event type, using your own receiver URL. Creation returns HTTP 200. Save its id and store the signing secret securely; it is shown once. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/endpoints \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: orders-endpoint-001" \ -d '{ "name": "Orders", "url": "https://hooks.example.com/orders", "event_types": [ "order.completed" ] }' ``` #### 4. Make your first request Send an event. Flowyn stores it and returns HTTP 202 before any delivery is attempted. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/events \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order-123-completed" \ -d '{ "type": "order.completed", "payload": { "order_id": "ord_123" } }' ``` Response: ```json { "id": "0f8e4a52-3c1d-4e7b-9a26-5d1b7c3e9f10", "environment": "test", "type": "order.completed", "status": "accepted", "attempt_count": 0, "request_id": "req_5f2a9c1e7b3d4a6f8e0c2b1d", "created_at": "2026-09-30T14:02:11Z" } ``` #### 5. Keep the id Every create returns an `id`, a UUID. It is how you, the dashboard, and your agent refer to this operation from now on. Every response also carries a `request_id` you can quote to support. #### 6. Read the status Read the resource back. Status changes are explicit, and every delivery attempt is recorded. bash: ```bash curl https://api.flowyn.dev/v1/events/0f8e4a52-3c1d-4e7b-9a26-5d1b7c3e9f10 \ -H "Authorization: Bearer $FLOWYN_API_KEY" curl https://api.flowyn.dev/v1/events/0f8e4a52-3c1d-4e7b-9a26-5d1b7c3e9f10/attempts \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` #### 7. Use the result For events, the result is the delivery status and its attempts. For jobs, the terminal status and the attempts. For documents, the output files of the file job. For monitors, the latest result and any open incident. For flags, the value and reason in the evaluation response itself. #### 8. Errors and retries A failed delivery or HTTP call does not fail your request: it is recorded as an attempt and retried within a finite policy. Errors in your own request come back as JSON with a `type`, a `code`, and a `message`. See [Errors](https://flowyn.dev/docs/getting-started/errors) and [Idempotency and retries](https://flowyn.dev/docs/getting-started/idempotency). > Delivery and execution are at-least-once. Send an `Idempotency-Key` on creates and deduplicate in receivers. #### Next step Pick a product: [Parse a PDF](https://flowyn.dev/docs/process/pdf-parse), [Run a durable HTTP job](https://flowyn.dev/docs/execute/jobs), [Deliver a webhook](https://flowyn.dev/docs/connect/events), [Monitor an endpoint](https://flowyn.dev/docs/observe/monitors), [Evaluate a flag](https://flowyn.dev/docs/configure/flags), or [Record an agent run](https://flowyn.dev/docs/agents/overview). Or try the [Playground](https://flowyn.dev/playground) first. ### Authentication: API keys, abilities, and environments URL: https://flowyn.dev/docs/getting-started/authentication How Flowyn API keys authenticate requests, how test and live environments are separated, what read, run, and reveal abilities allow, and how to rotate keys. #### How Flowyn authenticates requests Every request carries `Authorization: Bearer `. The key determines the workspace and the environment. The API does not use cookies. bash: ```bash curl https://api.flowyn.dev/v1/jobs \ -H "Authorization: Bearer flw_test_••••" ``` #### Test and live environments | Prefix | Environment | Behaviour | | --- | --- | --- | | flw_test_ | Test | Resources are separate from live. Use it while you build. | | flw_live_ | Live | Production traffic. Counts against the plan allowance. | #### Abilities A key can be limited to some abilities. A key created without a choice has all three. | Ability | Allows | | --- | --- | | read | GET requests. | | run | Creates and actions, such as sending events, creating jobs, recording agent runs, and testing a provider connection. Includes read. | | reveal | Everything, including changes that store or rotate secrets, such as provider connections and endpoint signing secrets. | #### Reveal-once keys A key is shown once when it is created. Flowyn stores only a hash. If a key is lost, create a new one and revoke the old one, or rotate it from the dashboard or with `POST /v1/api-keys/{id}/rotate`. #### Where keys belong Server-side only: a backend, a server function, a worker, an agent runtime, or a CI secret. A key in browser or mobile code can be extracted. #### Errors | Status | type | Meaning | | --- | --- | --- | | 401 | unauthorized | Missing, malformed, or revoked key. | | 403 | forbidden | The key lacks the ability the call needs (`ability_required`). | #### Next step [Idempotency and retries](https://flowyn.dev/docs/getting-started/idempotency). ### Idempotency and retries URL: https://flowyn.dev/docs/getting-started/idempotency How the Idempotency-Key header works on Flowyn creates, how Flowyn retries HTTP jobs and webhook deliveries, and how receivers should deduplicate. #### How idempotency works on create requests Send an `Idempotency-Key` header on creates such as `POST /v1/events`, `/v1/jobs`, `/v1/schedules`, `/v1/monitors`, document operations, and agent runs. If the same key arrives again with the same body within 24 hours, Flowyn returns the original response instead of creating a second resource. The same key with a different body returns `409 idempotency_mismatch`. http: ```http Idempotency-Key: order-123-completed ``` #### How Flowyn retries durable HTTP jobs A job attempt succeeds on a 2xx. Timeouts, connection errors, 408, 429, and 5xx responses are retried; other 4xx responses and blocked destinations fail without a retry. The wait doubles from about one second, varies by ±20%, and is capped at five minutes. A `Retry-After` from the destination is honoured. Jobs default to 5 attempts and accept up to 20. #### How Flowyn retries webhook deliveries A delivery gets up to 8 attempts. After a failed attempt Flowyn waits about 5 seconds, then 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours, and 10 hours, each varied by ±20%. | Attempt | Wait before (approx.) | | --- | --- | | 1 | immediate | | 2 | 5 s | | 3 | 5 min | | 4 | 30 min | | 5 | 2 h | | 6 | 5 h | | 7 | 10 h | | 8 | 10 h | #### At-least-once, so deduplicate A receiver may see the same request twice, for example when it processed a delivery but the response was lost. HTTP jobs send `Idempotency-Key: ` on every attempt. Webhook deliveries send `webhook-id: `, which stays the same across retries. Store the id and ignore repeats. #### What Flowyn does not retry Errors in your own API requests are returned to you, not retried. Fix the request and send it again with the same idempotency key. #### Next step [Errors](https://flowyn.dev/docs/getting-started/errors). ### Errors: shape, codes, and what to do URL: https://flowyn.dev/docs/getting-started/errors The Flowyn error response shape, the HTTP status codes the API returns, and how failed outbound work is represented as resources instead of errors. #### Error response shape Every API error is JSON with a stable `type`, a specific `code` when one applies, a human `message`, the `request_id`, and whether retrying can help. Error: ```json { "error": { "type": "invalid_request", "code": "invalid_timeout", "message": "timeout_ms must be between 100 and 30000.", "request_id": "req_5f2a9c1e7b3d4a6f8e0c2b1d", "class": "user", "retryable": false } } ``` #### Status codes | Status | type | Meaning | | --- | --- | --- | | 400 | invalid_request | The body is not valid JSON, a field has the wrong type, or the body is over 1 MiB (`body_too_large`). | | 401 | unauthorized | Missing, malformed, or revoked API key. | | 402 | billing_error | The monthly credit allowance is used (`credit_limit_exceeded`). Reads keep working. | | 403 | forbidden | The key lacks the ability this call needs (`ability_required`). | | 404 | not_found | The id does not exist in this environment. | | 409 | conflict | For example `idempotency_mismatch`: the Idempotency-Key was used with a different body. | | 422 | invalid_request | A field value is out of range, for example `invalid_timeout`. | | 429 | rate_limited | Too many requests (`rate_limit_exceeded`). Wait for `Retry-After` seconds. | | 5xx | server_error | Flowyn failed. The body carries an `error_id` to quote to support. | #### Recovery information Follow safe `next` guidance and use `Retry-After` before retrying a rate-limited request. Preserve `request_id`, `error_id`, and the original `Idempotency-Key` after a lost response. The SDK exposes `idempotencyKey` and `retryAfterSeconds`; CLI and MCP retain safe recovery fields. Canonical error documentation: https://flowyn.dev/docs/getting-started/errors. #### Failed operations are resources, not errors When an outbound HTTP call or a webhook delivery fails, your API request has already succeeded. The resource records the failed attempt and moves to `retrying`, then to its terminal status. Read the resource to see what happened. Repeated failures with the same fingerprint are grouped into an [Issue](https://flowyn.dev/docs/observe/issues). #### Next step Pick a product from the sidebar, or read the [REST reference](https://flowyn.dev/docs/tools/rest). ## Process · Documents ### PDF Parse URL: https://flowyn.dev/docs/process/pdf-parse Parses the text layer of a PDF into text, Markdown, or `flowyn.pdf_parse.v1` JSON. Headings, tables, and reading order are preserved. Pages without a text layer come back empty; Flowyn does not perform OCR. #### What pdf parse does Parses the text layer of a PDF into text, Markdown, or `flowyn.pdf_parse.v1` JSON. Headings, tables, and reading order are preserved. Pages without a text layer come back empty; Flowyn does not perform OCR. #### When to use pdf parse - You need the content of invoices, reports, or contracts as structured data - You feed documents to an LLM or an agent and want Markdown - You index PDFs for search #### Authentication Send an API key as a bearer token. Keys are shown once, belong in a server environment, and are bound to one environment: `flw_test_` keys act in test and `flw_live_` keys in live. http: ```http Authorization: Bearer flw_test_•••• ``` #### Endpoint http: ```http POST https://api.flowyn.dev/v1/pdf/parse ``` #### Request Send an `Idempotency-Key` so a retried request cannot create a duplicate. The body is JSON. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/pdf/parse \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: parse-quarterly-001" \ -d '{ "file_id": "9c1e5b7a-2d4f-4a6b-8e3c-1f0a7d2b5c64", "output": "markdown" }' ``` #### Response Flowyn returns HTTP 202. The response is abridged here; the [OpenAPI document](https://api.flowyn.dev/openapi/v1.yaml) lists every field. Response: ```json { "id": "2b7d9e1f-4a6c-4b8d-9f2e-7c1a3e5d8b90", "object": "job", "operation": "pdf_parse", "status": "queued", "file_id": "9c1e5b7a-2d4f-4a6b-8e3c-1f0a7d2b5c64", "request_id": "req_5f2a9c1e7b3d4a6f8e0c2b1d" } ``` #### Lifecycle 1. Upload the file with `POST /v1/files` (tus resumable upload). The response is the file with its `id`. 2. Call `POST /v1/pdf/parse` with the `file_id`. Flowyn stores the operation and returns HTTP 202 with a job id before any work starts. 3. A managed worker claims the job, runs the operation, and records an attempt. 4. Read `GET /v1/file-jobs/{id}`. When `status` is `complete`, `outputs` lists the produced files; download one with `GET /v1/files/{id}/download`. #### Status values | Status | Meaning | | --- | --- | | queued | Accepted, waiting for a worker. | | delayed | Waiting before a scheduled retry. | | processing | A worker is running the operation. | | retrying | The last attempt failed and another is scheduled. | | complete | Outputs are stored and listed in `outputs`. | | failed | The operation could not complete; `error` explains why. | | cancelled | Cancelled before it completed. | #### Retries A file operation gets up to 3 attempts. Worker failures and timeouts are retried; an unreadable or unsupported file fails without a retry. #### Idempotency Send `Idempotency-Key`. The same key and body return the original job. #### Errors | Status | type | Meaning | | --- | --- | --- | | 400 | invalid_request | The body is not valid JSON, a field has the wrong type, or the body is over 1 MiB (`body_too_large`). | | 401 | unauthorized | Missing, malformed, or revoked API key. | | 402 | billing_error | The monthly credit allowance is used (`credit_limit_exceeded`). Reads keep working. | | 403 | forbidden | The key lacks the ability this call needs (`ability_required`). | | 404 | not_found | The id does not exist in this environment. | | 409 | conflict | For example `idempotency_mismatch`: the Idempotency-Key was used with a different body. | | 422 | invalid_request | A field value is out of range, for example `invalid_timeout`. | | 429 | rate_limited | Too many requests (`rate_limit_exceeded`). Wait for `Retry-After` seconds. | | 5xx | server_error | Flowyn failed. The body carries an `error_id` to quote to support. | #### Limits - `output` is `text`, `markdown`, or `json` - Files up to 50 MB and 200 pages - No OCR: image-only pages return empty content #### Upload the file first Uploads use the tus resumable upload protocol. One request can create the upload and send the bytes. bash: ```bash curl -i -X POST https://api.flowyn.dev/v1/files \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Tus-Resumable: 1.0.0" \ -H "Upload-Length: $(wc -c < report.pdf | tr -d ' ')" \ -H "Upload-Metadata: filename $(printf report.pdf | base64),filetype $(printf application/pdf | base64)" \ -H "Content-Type: application/offset+octet-stream" \ --data-binary @report.pdf ``` #### Read it back Keep the id and read the resource whenever you need its current state. bash: ```bash curl https://api.flowyn.dev/v1/file-jobs/2b7d9e1f-4a6c-4b8d-9f2e-7c1a3e5d8b90 \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` #### Related - [DOCX Parse](https://flowyn.dev/docs/process/docx-parse) - [Intelligent PDF Splitter](https://flowyn.dev/docs/process/split-pdf) - [REST reference](https://flowyn.dev/docs/tools/rest) #### Next step Try it in the [Playground](https://flowyn.dev/playground?op=process) with a sample PDF. ### HTML to PDF URL: https://flowyn.dev/docs/process/html-to-pdf Renders an uploaded HTML file in an isolated browser worker and stores the result as a PDF output of the job. #### What html to pdf does Renders an uploaded HTML file in an isolated browser worker and stores the result as a PDF output of the job. #### When to use html to pdf - Invoices and receipts generated from templates - Statements and exports - Report snapshots #### Authentication Send an API key as a bearer token. Keys are shown once, belong in a server environment, and are bound to one environment: `flw_test_` keys act in test and `flw_live_` keys in live. http: ```http Authorization: Bearer flw_test_•••• ``` #### Endpoint http: ```http POST https://api.flowyn.dev/v1/pdf/from-html ``` #### Request Send an `Idempotency-Key` so a retried request cannot create a duplicate. The body is JSON. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/pdf/from-html \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: invoice-1042-pdf" \ -d '{ "file_id": "9c1e5b7a-2d4f-4a6b-8e3c-1f0a7d2b5c64" }' ``` #### Response Flowyn returns HTTP 202. The response is abridged here; the [OpenAPI document](https://api.flowyn.dev/openapi/v1.yaml) lists every field. Response: ```json { "id": "2b7d9e1f-4a6c-4b8d-9f2e-7c1a3e5d8b90", "object": "job", "operation": "html_pdf", "status": "queued", "file_id": "9c1e5b7a-2d4f-4a6b-8e3c-1f0a7d2b5c64", "request_id": "req_5f2a9c1e7b3d4a6f8e0c2b1d" } ``` #### Lifecycle 1. Upload the file with `POST /v1/files` (tus resumable upload). The response is the file with its `id`. 2. Call `POST /v1/pdf/from-html` with the `file_id`. Flowyn stores the operation and returns HTTP 202 with a job id before any work starts. 3. A managed worker claims the job, runs the operation, and records an attempt. 4. Read `GET /v1/file-jobs/{id}`. When `status` is `complete`, `outputs` lists the produced files; download one with `GET /v1/files/{id}/download`. #### Status values | Status | Meaning | | --- | --- | | queued | Accepted, waiting for a worker. | | delayed | Waiting before a scheduled retry. | | processing | A worker is running the operation. | | retrying | The last attempt failed and another is scheduled. | | complete | Outputs are stored and listed in `outputs`. | | failed | The operation could not complete; `error` explains why. | | cancelled | Cancelled before it completed. | #### Retries A file operation gets up to 3 attempts. Worker failures and timeouts are retried; an unreadable or unsupported file fails without a retry. #### Idempotency Send `Idempotency-Key` so a retried request cannot render the same invoice twice. #### Errors | Status | type | Meaning | | --- | --- | --- | | 400 | invalid_request | The body is not valid JSON, a field has the wrong type, or the body is over 1 MiB (`body_too_large`). | | 401 | unauthorized | Missing, malformed, or revoked API key. | | 402 | billing_error | The monthly credit allowance is used (`credit_limit_exceeded`). Reads keep working. | | 403 | forbidden | The key lacks the ability this call needs (`ability_required`). | | 404 | not_found | The id does not exist in this environment. | | 409 | conflict | For example `idempotency_mismatch`: the Idempotency-Key was used with a different body. | | 422 | invalid_request | A field value is out of range, for example `invalid_timeout`. | | 429 | rate_limited | Too many requests (`rate_limit_exceeded`). Wait for `Retry-After` seconds. | | 5xx | server_error | Flowyn failed. The body carries an `error_id` to quote to support. | #### Limits - The source must be an uploaded HTML file (`text/html`) - Use a complete document beginning with ``; a bare HTML fragment may be detected as plain text and rejected - Files up to 50 MB - Output is one stored PDF file #### Read it back Keep the id and read the resource whenever you need its current state. bash: ```bash curl https://api.flowyn.dev/v1/file-jobs/2b7d9e1f-4a6c-4b8d-9f2e-7c1a3e5d8b90 \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` #### Related - [PDF Parse](https://flowyn.dev/docs/process/pdf-parse) - [Documents & files](https://flowyn.dev/process) #### Next step See [Durable HTTP jobs](https://flowyn.dev/docs/execute/jobs) for how other asynchronous work reports status. ### DOCX Parse URL: https://flowyn.dev/docs/process/docx-parse Reads a DOCX in document order and returns Markdown or `flowyn.docx_parse.v1` JSON. Paragraphs, headings, lists, and tables come through in order. #### What docx parse does Reads a DOCX in document order and returns Markdown or `flowyn.docx_parse.v1` JSON. Paragraphs, headings, lists, and tables come through in order. #### When to use docx parse - Contract intake - Content migration - Feeding Word documents to an agent #### Authentication Send an API key as a bearer token. Keys are shown once, belong in a server environment, and are bound to one environment: `flw_test_` keys act in test and `flw_live_` keys in live. http: ```http Authorization: Bearer flw_test_•••• ``` #### Endpoint http: ```http POST https://api.flowyn.dev/v1/docx/parse ``` #### Request Send an `Idempotency-Key` so a retried request cannot create a duplicate. The body is JSON. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/docx/parse \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: docx-001" \ -d '{ "file_id": "9c1e5b7a-2d4f-4a6b-8e3c-1f0a7d2b5c64", "output": "markdown" }' ``` #### Response Flowyn returns HTTP 202. The response is abridged here; the [OpenAPI document](https://api.flowyn.dev/openapi/v1.yaml) lists every field. Response: ```json { "id": "2b7d9e1f-4a6c-4b8d-9f2e-7c1a3e5d8b90", "object": "job", "operation": "docx_parse", "status": "queued", "file_id": "9c1e5b7a-2d4f-4a6b-8e3c-1f0a7d2b5c64", "request_id": "req_5f2a9c1e7b3d4a6f8e0c2b1d" } ``` #### Lifecycle 1. Upload the file with `POST /v1/files` (tus resumable upload). The response is the file with its `id`. 2. Call `POST /v1/docx/parse` with the `file_id`. Flowyn stores the operation and returns HTTP 202 with a job id before any work starts. 3. A managed worker claims the job, runs the operation, and records an attempt. 4. Read `GET /v1/file-jobs/{id}`. When `status` is `complete`, `outputs` lists the produced files; download one with `GET /v1/files/{id}/download`. #### Status values | Status | Meaning | | --- | --- | | queued | Accepted, waiting for a worker. | | delayed | Waiting before a scheduled retry. | | processing | A worker is running the operation. | | retrying | The last attempt failed and another is scheduled. | | complete | Outputs are stored and listed in `outputs`. | | failed | The operation could not complete; `error` explains why. | | cancelled | Cancelled before it completed. | #### Retries A file operation gets up to 3 attempts. Worker failures and timeouts are retried; an unreadable or unsupported file fails without a retry. #### Idempotency Send `Idempotency-Key`. #### Errors | Status | type | Meaning | | --- | --- | --- | | 400 | invalid_request | The body is not valid JSON, a field has the wrong type, or the body is over 1 MiB (`body_too_large`). | | 401 | unauthorized | Missing, malformed, or revoked API key. | | 402 | billing_error | The monthly credit allowance is used (`credit_limit_exceeded`). Reads keep working. | | 403 | forbidden | The key lacks the ability this call needs (`ability_required`). | | 404 | not_found | The id does not exist in this environment. | | 409 | conflict | For example `idempotency_mismatch`: the Idempotency-Key was used with a different body. | | 422 | invalid_request | A field value is out of range, for example `invalid_timeout`. | | 429 | rate_limited | Too many requests (`rate_limit_exceeded`). Wait for `Retry-After` seconds. | | 5xx | server_error | Flowyn failed. The body carries an `error_id` to quote to support. | #### Limits - `output` is `markdown` or `json` - Files up to 50 MB - Flowyn does not infer business fields from the document #### Read it back Keep the id and read the resource whenever you need its current state. bash: ```bash curl https://api.flowyn.dev/v1/file-jobs/2b7d9e1f-4a6c-4b8d-9f2e-7c1a3e5d8b90 \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` #### Related - [PDF Parse](https://flowyn.dev/docs/process/pdf-parse) #### Next step Read [Limits and credits](https://flowyn.dev/docs/tools/limits). ### Intelligent PDF Splitter URL: https://flowyn.dev/docs/process/split-pdf Scans a mixed PDF for document boundaries and splits it into child PDFs, each with its page range and a confidence score. Low-confidence groups are marked for review. Use `POST /v1/pdf/split` instead when you already know the page ranges. #### What intelligent pdf splitter does Scans a mixed PDF for document boundaries and splits it into child PDFs, each with its page range and a confidence score. Low-confidence groups are marked for review. Use `POST /v1/pdf/split` instead when you already know the page ranges. #### When to use intelligent pdf splitter - Scanned intake batches - Mailroom digitisation - Archive cleanup #### Authentication Send an API key as a bearer token. Keys are shown once, belong in a server environment, and are bound to one environment: `flw_test_` keys act in test and `flw_live_` keys in live. http: ```http Authorization: Bearer flw_test_•••• ``` #### Endpoint http: ```http POST https://api.flowyn.dev/v1/split-pdf ``` #### Request Send an `Idempotency-Key` so a retried request cannot create a duplicate. The body is JSON. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/split-pdf \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: split-batch-16" \ -d '{ "file_id": "9c1e5b7a-2d4f-4a6b-8e3c-1f0a7d2b5c64" }' ``` #### Response Flowyn returns HTTP 202. The response is abridged here; the [OpenAPI document](https://api.flowyn.dev/openapi/v1.yaml) lists every field. Response: ```json { "id": "2b7d9e1f-4a6c-4b8d-9f2e-7c1a3e5d8b90", "object": "job", "operation": "split_pdf", "status": "queued", "file_id": "9c1e5b7a-2d4f-4a6b-8e3c-1f0a7d2b5c64", "request_id": "req_5f2a9c1e7b3d4a6f8e0c2b1d" } ``` #### Lifecycle 1. Upload the file with `POST /v1/files` (tus resumable upload). The response is the file with its `id`. 2. Call `POST /v1/split-pdf` with the `file_id`. Flowyn stores the operation and returns HTTP 202 with a job id before any work starts. 3. A managed worker claims the job, runs the operation, and records an attempt. 4. Read `GET /v1/file-jobs/{id}`. When `status` is `complete`, `outputs` lists the produced files; download one with `GET /v1/files/{id}/download`. #### Status values | Status | Meaning | | --- | --- | | queued | Accepted, waiting for a worker. | | delayed | Waiting before a scheduled retry. | | processing | A worker is running the operation. | | retrying | The last attempt failed and another is scheduled. | | complete | Outputs are stored and listed in `outputs`. | | failed | The operation could not complete; `error` explains why. | | cancelled | Cancelled before it completed. | #### Retries A file operation gets up to 3 attempts. Worker failures and timeouts are retried; an unreadable or unsupported file fails without a retry. #### Idempotency Send `Idempotency-Key`. #### Errors | Status | type | Meaning | | --- | --- | --- | | 400 | invalid_request | The body is not valid JSON, a field has the wrong type, or the body is over 1 MiB (`body_too_large`). | | 401 | unauthorized | Missing, malformed, or revoked API key. | | 402 | billing_error | The monthly credit allowance is used (`credit_limit_exceeded`). Reads keep working. | | 403 | forbidden | The key lacks the ability this call needs (`ability_required`). | | 404 | not_found | The id does not exist in this environment. | | 409 | conflict | For example `idempotency_mismatch`: the Idempotency-Key was used with a different body. | | 422 | invalid_request | A field value is out of range, for example `invalid_timeout`. | | 429 | rate_limited | Too many requests (`rate_limit_exceeded`). Wait for `Retry-After` seconds. | | 5xx | server_error | Flowyn failed. The body carries an `error_id` to quote to support. | #### Limits - Files up to 50 MB and 200 pages - Confidence is always reported; low-confidence boundaries are still returned and marked #### Read it back Keep the id and read the resource whenever you need its current state. bash: ```bash curl https://api.flowyn.dev/v1/file-jobs/2b7d9e1f-4a6c-4b8d-9f2e-7c1a3e5d8b90 \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` #### Related - [PDF Parse](https://flowyn.dev/docs/process/pdf-parse) #### Next step Combine with [Events](https://flowyn.dev/docs/connect/events) to notify another system when a split completes. ## Execute · HTTP & schedules ### Durable HTTP Jobs URL: https://flowyn.dev/docs/execute/jobs Stores an HTTP request, executes it on managed workers, retries failures within a finite policy, and keeps every attempt with a terminal status. #### What durable http jobs does Stores an HTTP request, executes it on managed workers, retries failures within a finite policy, and keeps every attempt with a terminal status. #### When to use durable http jobs - Calling a flaky third-party API - Work that must outlive the web request - Calling an agent's HTTP endpoint with retries #### Authentication Send an API key as a bearer token. Keys are shown once, belong in a server environment, and are bound to one environment: `flw_test_` keys act in test and `flw_live_` keys in live. http: ```http Authorization: Bearer flw_test_•••• ``` #### Endpoint http: ```http POST https://api.flowyn.dev/v1/jobs ``` #### Request Send an `Idempotency-Key` so a retried request cannot create a duplicate. The body is JSON. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/jobs \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: sync-invoice-881" \ -d '{ "url": "https://api.example.com/reports/generate", "method": "POST", "headers": { "Content-Type": "application/json" }, "body": "{\"report\":\"monthly\"}", "timeout_ms": 10000, "max_attempts": 5 }' ``` #### Response Flowyn returns HTTP 202. The response is abridged here; the [OpenAPI document](https://api.flowyn.dev/openapi/v1.yaml) lists every field. Response: ```json { "id": "6a2f1c9e-8b4d-4f3a-a5e7-2c9d0b1e4f83", "environment": "test", "kind": "http", "status": "queued", "method": "POST", "url": "https://api.example.com/reports/generate", "attempt_count": 0, "max_attempts": 5, "run_at": "2026-09-30T09:14:02Z", "request_id": "req_5f2a9c1e7b3d4a6f8e0c2b1d", "created_at": "2026-09-30T09:14:02Z" } ``` #### Lifecycle 1. `POST /v1/jobs` stores the call and returns HTTP 202. Nothing has been sent yet. 2. A worker claims the job with a lease and makes the request. 3. A 2xx completes the job. A timeout, connection error, 408, 429, or 5xx records a failed attempt and schedules a retry. 4. Other 4xx responses fail the job without a retry. 5. When every attempt fails, the job ends `exhausted`. Read the attempts with `GET /v1/jobs/{id}/attempts`. #### Status values | Status | Meaning | | --- | --- | | queued | Stored, waiting for a worker. | | delayed | Waiting for `run_at` or a retry delay. | | running | An attempt is in flight. | | retrying | The last attempt failed and another is scheduled. | | completed | A 2xx response was received. | | failed | A response that is not retried, such as a 404. | | exhausted | Every attempt failed. | | cancelled | Cancelled with `POST /v1/jobs/{id}/cancel`. | #### Retries The wait doubles from about one second with ±20% variation, capped at five minutes. A `Retry-After` header is honoured. #### Idempotency Send `Idempotency-Key` on create. Each outbound attempt carries `Idempotency-Key: ` so the destination can deduplicate. #### Errors | Status | type | Meaning | | --- | --- | --- | | 400 | invalid_request | The body is not valid JSON, a field has the wrong type, or the body is over 1 MiB (`body_too_large`). | | 401 | unauthorized | Missing, malformed, or revoked API key. | | 402 | billing_error | The monthly credit allowance is used (`credit_limit_exceeded`). Reads keep working. | | 403 | forbidden | The key lacks the ability this call needs (`ability_required`). | | 404 | not_found | The id does not exist in this environment. | | 409 | conflict | For example `idempotency_mismatch`: the Idempotency-Key was used with a different body. | | 422 | invalid_request | A field value is out of range, for example `invalid_timeout`. | | 429 | rate_limited | Too many requests (`rate_limit_exceeded`). Wait for `Retry-After` seconds. | | 5xx | server_error | Flowyn failed. The body carries an `error_id` to quote to support. | #### Limits - `timeout_ms` 100–30,000 (default 10,000) - `max_attempts` 1–20 (default 5) - `body` is a string of up to 65,536 characters - Up to 16 headers #### Read it back Keep the id and read the resource whenever you need its current state. bash: ```bash curl https://api.flowyn.dev/v1/jobs/6a2f1c9e-8b4d-4f3a-a5e7-2c9d0b1e4f83 \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` #### Related - [Schedules](https://flowyn.dev/docs/execute/schedules) - [Events and webhooks](https://flowyn.dev/docs/connect/events) #### Next step Run the same call on a cron with [Schedules](https://flowyn.dev/docs/execute/schedules). ### Schedules URL: https://flowyn.dev/docs/execute/schedules Stores a cron expression, a timezone, and an HTTP request. Each occurrence creates an ordinary durable job with its own attempts and retries. Missed occurrences are skipped, not backfilled. #### What schedules does Stores a cron expression, a timezone, and an HTTP request. Each occurrence creates an ordinary durable job with its own attempts and retries. Missed occurrences are skipped, not backfilled. #### When to use schedules - Nightly reconciliation - Periodic syncs - Waking an agent on a timetable #### Authentication Send an API key as a bearer token. Keys are shown once, belong in a server environment, and are bound to one environment: `flw_test_` keys act in test and `flw_live_` keys in live. http: ```http Authorization: Bearer flw_test_•••• ``` #### Endpoint http: ```http POST https://api.flowyn.dev/v1/schedules ``` #### Request Send an `Idempotency-Key` so a retried request cannot create a duplicate. The body is JSON. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/schedules \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: nightly-report" \ -d '{ "name": "Nightly report", "cron": "0 2 * * *", "timezone": "Europe/Berlin", "method": "POST", "url": "https://api.example.com/reports/nightly", "max_attempts": 3 }' ``` #### Response Flowyn returns HTTP 200. The response is abridged here; the [OpenAPI document](https://api.flowyn.dev/openapi/v1.yaml) lists every field. Response: ```json { "id": "d4a1b7c9-6e2f-4c8a-b3d5-9f1e0a2c4b76", "environment": "test", "name": "Nightly report", "cron": "0 2 * * *", "timezone": "Europe/Berlin", "method": "POST", "url": "https://api.example.com/reports/nightly", "max_attempts": 3, "paused": false, "catch_up": "skip", "overlap_policy": "skip", "next_run_at": "2026-10-01T00:00:00Z", "last_run_at": null } ``` #### Lifecycle 1. `POST /v1/schedules` stores the schedule. Flowyn computes `next_run_at`. 2. At each occurrence Flowyn creates a durable HTTP job from the schedule's request. 3. The job runs with its own attempts and retries. 4. With the default `overlap_policy: skip`, an occurrence is skipped while the previous one is still running. #### Status values | Status | Meaning | | --- | --- | | active (`paused: false`) | Creates a job at each occurrence. | | paused (`paused: true`) | Kept, creates nothing. | #### Retries Retries belong to each generated job and follow the [job retry policy](https://flowyn.dev/docs/execute/jobs). #### Idempotency Send `Idempotency-Key` on create. #### Errors | Status | type | Meaning | | --- | --- | --- | | 400 | invalid_request | The body is not valid JSON, a field has the wrong type, or the body is over 1 MiB (`body_too_large`). | | 401 | unauthorized | Missing, malformed, or revoked API key. | | 402 | billing_error | The monthly credit allowance is used (`credit_limit_exceeded`). Reads keep working. | | 403 | forbidden | The key lacks the ability this call needs (`ability_required`). | | 404 | not_found | The id does not exist in this environment. | | 409 | conflict | For example `idempotency_mismatch`: the Idempotency-Key was used with a different body. | | 422 | invalid_request | A field value is out of range, for example `invalid_timeout`. | | 429 | rate_limited | Too many requests (`rate_limit_exceeded`). Wait for `Retry-After` seconds. | | 5xx | server_error | Flowyn failed. The body carries an `error_id` to quote to support. | #### Limits - Five-field cron and an IANA timezone - `catch_up` is `skip`: missed occurrences are not backfilled - Same request limits as HTTP jobs #### Read it back Keep the id and read the resource whenever you need its current state. bash: ```bash curl https://api.flowyn.dev/v1/schedules/d4a1b7c9-6e2f-4c8a-b3d5-9f1e0a2c4b76/occurrences \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` #### Related - [Durable HTTP Jobs](https://flowyn.dev/docs/execute/jobs) #### Next step Watch the target with a [Monitor](https://flowyn.dev/docs/observe/monitors) or a [Heartbeat](https://flowyn.dev/docs/observe/heartbeats). ## Connect · Events ### Events and webhooks URL: https://flowyn.dev/docs/connect/events Stores an event, signs and delivers it to every enabled endpoint subscribed to its type, retries failed deliveries within a finite policy, records every attempt, and supports replay. #### What events and webhooks does Stores an event, signs and delivers it to every enabled endpoint subscribed to its type, retries failed deliveries within a finite policy, records every attempt, and supports replay. #### When to use events and webhooks - Order and payment notifications - Customer-facing webhooks - Sending an agent's results to other systems #### Authentication Send an API key as a bearer token. Keys are shown once, belong in a server environment, and are bound to one environment: `flw_test_` keys act in test and `flw_live_` keys in live. http: ```http Authorization: Bearer flw_test_•••• ``` #### Endpoint http: ```http POST https://api.flowyn.dev/v1/events ``` #### Request Send an `Idempotency-Key` so a retried request cannot create a duplicate. The body is JSON. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/events \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order-123-completed" \ -d '{ "type": "order.completed", "payload": { "order_id": "ord_123", "total": 4900 } }' ``` #### Response Flowyn returns HTTP 202. The response is abridged here; the [OpenAPI document](https://api.flowyn.dev/openapi/v1.yaml) lists every field. Response: ```json { "id": "0f8e4a52-3c1d-4e7b-9a26-5d1b7c3e9f10", "environment": "test", "type": "order.completed", "status": "accepted", "attempt_count": 0, "request_id": "req_5f2a9c1e7b3d4a6f8e0c2b1d", "created_at": "2026-09-30T14:02:11Z" } ``` #### Lifecycle 1. `POST /v1/events` stores the event and returns HTTP 202. 2. Flowyn signs the exact body with each endpoint's secret and POSTs it. The response is attempt 1. 3. A failed attempt schedules the next one within the policy. 4. A 2xx marks the delivery `delivered`. Attempts stay attached to the event. 5. `POST /v1/events/{id}/replay` sends it again as a new delivery without touching history. #### Status values | Status | Meaning | | --- | --- | | accepted | Stored, no delivery yet. | | delivering | Delivery is in progress or a retry is scheduled. | | delivered | A 2xx was received. | | failed | Every attempt failed. Replay is available. | | cancelled | Cancelled before delivery finished. | #### Retries Up to 8 attempts per delivery, waiting about 5 s, 5 min, 30 min, 2 h, 5 h, 10 h, and 10 h between them (±20%). Repeated failures surface as an Issue. #### Idempotency Send `Idempotency-Key` on create. Each delivery carries `webhook-id` (the event id, stable across retries); deduplicate on it. #### Errors | Status | type | Meaning | | --- | --- | --- | | 400 | invalid_request | The body is not valid JSON, a field has the wrong type, or the body is over 1 MiB (`body_too_large`). | | 401 | unauthorized | Missing, malformed, or revoked API key. | | 402 | billing_error | The monthly credit allowance is used (`credit_limit_exceeded`). Reads keep working. | | 403 | forbidden | The key lacks the ability this call needs (`ability_required`). | | 404 | not_found | The id does not exist in this environment. | | 409 | conflict | For example `idempotency_mismatch`: the Idempotency-Key was used with a different body. | | 422 | invalid_request | A field value is out of range, for example `invalid_timeout`. | | 429 | rate_limited | Too many requests (`rate_limit_exceeded`). Wait for `Retry-After` seconds. | | 5xx | server_error | Flowyn failed. The body carries an `error_id` to quote to support. | #### Limits - `payload` must be a JSON object; request bodies up to 1 MiB - Endpoints must be HTTPS - One event matches at most 50 endpoints #### How to verify a signed delivery Deliveries follow the Standard Webhooks format. Each request carries `webhook-id`, `webhook-timestamp`, and `webhook-signature`. The signature is `v1,` followed by the base64 HMAC-SHA256 of `{webhook-id}.{webhook-timestamp}.{raw body}`, keyed with the base64-decoded part of your `whsec_` secret. During a secret rotation the header can hold several space-separated signatures; accept the request if any one matches. js: ```js import { createHmac, timingSafeEqual } from "node:crypto"; export function verify(rawBody, headers, secret) { const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64"); const signed = `${headers["webhook-id"]}.${headers["webhook-timestamp"]}.${rawBody}`; const expected = Buffer.from(createHmac("sha256", key).update(signed).digest("base64")); return headers["webhook-signature"].split(" ").some((sig) => { const actual = Buffer.from(sig.replace(/^v1,/, "")); return actual.length === expected.length && timingSafeEqual(actual, expected); }); } ``` Reject old timestamps to limit replays, for example anything more than five minutes from your clock. #### Read it back Keep the id and read the resource whenever you need its current state. bash: ```bash curl https://api.flowyn.dev/v1/events/0f8e4a52-3c1d-4e7b-9a26-5d1b7c3e9f10/attempts \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` #### Related - [Integrations and endpoints](https://flowyn.dev/docs/connect/integrations) - [Issues](https://flowyn.dev/docs/observe/issues) - [Reliable webhooks guide](https://flowyn.dev/docs/guides/how-webhook-retries-work) #### Next step Configure a destination in [Integrations](https://flowyn.dev/docs/connect/integrations). ### Integrations and endpoints URL: https://flowyn.dev/docs/connect/integrations Creates an HTTPS destination for chosen event types, returns its signing secret once, and sends test deliveries whose results are recorded. #### What integrations and endpoints does Creates an HTTPS destination for chosen event types, returns its signing secret once, and sends test deliveries whose results are recorded. #### When to use integrations and endpoints - Connecting a partner system - Validating a new receiver - Staging before live #### Authentication Send an API key as a bearer token. Keys are shown once, belong in a server environment, and are bound to one environment: `flw_test_` keys act in test and `flw_live_` keys in live. http: ```http Authorization: Bearer flw_test_•••• ``` #### Endpoint http: ```http POST https://api.flowyn.dev/v1/endpoints ``` #### Request Send an `Idempotency-Key` so a retried request cannot create a duplicate. The body is JSON. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/endpoints \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: ep-orders" \ -d '{ "name": "Orders destination", "url": "https://hooks.example.com/orders", "event_types": [ "order.completed" ] }' ``` #### Response Flowyn returns HTTP 200. The response is abridged here; the [OpenAPI document](https://api.flowyn.dev/openapi/v1.yaml) lists every field. Response: ```json { "id": "5f9b2d7e-3a1c-4d6f-8b0e-4c2a9e7d1f38", "environment": "test", "name": "Orders destination", "url": "https://hooks.example.com/orders", "event_types": [ "order.completed" ], "disabled": false, "signing_secret": "whsec_•••• (shown once)", "signing_secret_hint": "whsec_…a1b2" } ``` #### Lifecycle 1. Create the endpoint. `signing_secret` is returned once; store it. 2. `POST /v1/endpoints/{id}/test` sends a test delivery and records its result. 3. Events whose type is in `event_types` are delivered to the endpoint. 4. Rotate the secret with `POST /v1/endpoints/{id}/rotate-secret`, or disable the endpoint to stop deliveries without deleting history. #### Status values | Status | Meaning | | --- | --- | | enabled (`disabled: false`) | Receives deliveries. | | disabled (`disabled: true`) | Kept, receives no deliveries. | #### Retries Deliveries to the endpoint follow the event retry policy. #### Idempotency Send `Idempotency-Key` on create. #### Errors | Status | type | Meaning | | --- | --- | --- | | 400 | invalid_request | The body is not valid JSON, a field has the wrong type, or the body is over 1 MiB (`body_too_large`). | | 401 | unauthorized | Missing, malformed, or revoked API key. | | 402 | billing_error | The monthly credit allowance is used (`credit_limit_exceeded`). Reads keep working. | | 403 | forbidden | The key lacks the ability this call needs (`ability_required`). | | 404 | not_found | The id does not exist in this environment. | | 409 | conflict | For example `idempotency_mismatch`: the Idempotency-Key was used with a different body. | | 422 | invalid_request | A field value is out of range, for example `invalid_timeout`. | | 429 | rate_limited | Too many requests (`rate_limit_exceeded`). Wait for `Retry-After` seconds. | | 5xx | server_error | Flowyn failed. The body carries an `error_id` to quote to support. | #### Limits - HTTPS URLs only - `timeout_ms` 100–30,000 - Optional `max_sends_per_minute` 1–600 #### Read it back Keep the id and read the resource whenever you need its current state. bash: ```bash curl https://api.flowyn.dev/v1/endpoints/5f9b2d7e-3a1c-4d6f-8b0e-4c2a9e7d1f38 \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` #### Related - [Events and webhooks](https://flowyn.dev/docs/connect/events) #### Next step Watch the receiver itself with [Monitors](https://flowyn.dev/docs/observe/monitors). ## Observe · Monitors & issues ### HTTP monitors URL: https://flowyn.dev/docs/observe/monitors Requests a URL on a fixed interval, records each result with status and latency, opens an incident after three consecutive failed results, and records recovery on the next pass. #### What http monitors does Requests a URL on a fixed interval, records each result with status and latency, opens an incident after three consecutive failed results, and records recovery on the next pass. #### When to use http monitors - Public API health - An agent's HTTP endpoint - Staging smoke checks #### Authentication Send an API key as a bearer token. Keys are shown once, belong in a server environment, and are bound to one environment: `flw_test_` keys act in test and `flw_live_` keys in live. http: ```http Authorization: Bearer flw_test_•••• ``` #### Endpoint http: ```http POST https://api.flowyn.dev/v1/monitors ``` #### Request Send an `Idempotency-Key` so a retried request cannot create a duplicate. The body is JSON. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/monitors \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: monitor-prod-health" \ -d '{ "name": "Production API", "type": "http", "method": "GET", "url": "https://api.example.com/health", "interval_seconds": 60, "timeout_ms": 5000, "assertions": [ { "source": "status_code", "operator": "equals", "expected": 200 } ] }' ``` #### Response Flowyn returns HTTP 201. The response is abridged here; the [OpenAPI document](https://api.flowyn.dev/openapi/v1.yaml) lists every field. Response: ```json { "id": "8e3c5a7b-1d9f-4e2a-b6c4-0a7f3d1e9b52", "environment": "test", "name": "Production API", "type": "http", "enabled": true, "paused": false, "current_status": "waiting", "method": "GET", "url": "https://api.example.com/health", "interval_seconds": 60, "timeout_ms": 5000, "last_checked_at": null } ``` #### Lifecycle 1. Create the monitor. The first check runs within the interval. 2. Each check stores a result with status, latency, and evidence for failed assertions. 3. Three consecutive failed results open an incident. 4. The next passing result closes the incident as recovered. Read incidents with `GET /v1/monitors/{id}/incidents`. #### Status values | Status | Meaning | | --- | --- | | waiting | No result yet. | | healthy | The latest result passed. | | degraded | Results are mixed. | | failing | Recent results failed. | | paused | Not checking. | #### Retries Monitors do not retry a check; they check again at the next interval. #### Idempotency Send `Idempotency-Key` on create. #### Errors | Status | type | Meaning | | --- | --- | --- | | 400 | invalid_request | The body is not valid JSON, a field has the wrong type, or the body is over 1 MiB (`body_too_large`). | | 401 | unauthorized | Missing, malformed, or revoked API key. | | 402 | billing_error | The monthly credit allowance is used (`credit_limit_exceeded`). Reads keep working. | | 403 | forbidden | The key lacks the ability this call needs (`ability_required`). | | 404 | not_found | The id does not exist in this environment. | | 409 | conflict | For example `idempotency_mismatch`: the Idempotency-Key was used with a different body. | | 422 | invalid_request | A field value is out of range, for example `invalid_timeout`. | | 429 | rate_limited | Too many requests (`rate_limit_exceeded`). Wait for `Retry-After` seconds. | | 5xx | server_error | Flowyn failed. The body carries an `error_id` to quote to support. | #### Limits - `interval_seconds` 30–86,400 - `timeout_ms` 100–30,000 - Up to 20 assertions #### Read it back Keep the id and read the resource whenever you need its current state. bash: ```bash curl https://api.flowyn.dev/v1/monitors/8e3c5a7b-1d9f-4e2a-b6c4-0a7f3d1e9b52/results \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` #### Related - [Heartbeats](https://flowyn.dev/docs/observe/heartbeats) - [API checks](https://flowyn.dev/docs/observe/api-checks) - [Issues](https://flowyn.dev/docs/observe/issues) #### Next step Watch processes that cannot be polled with [Heartbeats](https://flowyn.dev/docs/observe/heartbeats). ### Heartbeats URL: https://flowyn.dev/docs/observe/heartbeats Expects a ping from your process. If no ping arrives within twice `interval_seconds`, Flowyn records a missed heartbeat. Three consecutive misses open an incident; the next ping closes it. #### What heartbeats does Expects a ping from your process. If no ping arrives within twice `interval_seconds`, Flowyn records a missed heartbeat. Three consecutive misses open an incident; the next ping closes it. #### When to use heartbeats - Nightly cron jobs - Queue workers - Agents that run on a timetable #### Authentication Send an API key as a bearer token. Keys are shown once, belong in a server environment, and are bound to one environment: `flw_test_` keys act in test and `flw_live_` keys in live. http: ```http Authorization: Bearer flw_test_•••• ``` #### Endpoint http: ```http POST https://api.flowyn.dev/v1/monitors ``` #### Request Send an `Idempotency-Key` so a retried request cannot create a duplicate. The body is JSON. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/monitors \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: hb-nightly-backup" \ -d '{ "name": "Nightly backup", "type": "heartbeat", "method": "GET", "url": "", "interval_seconds": 86400, "timeout_ms": 1000, "assertions": [] }' ``` #### Response Flowyn returns HTTP 201. The response is abridged here; the [OpenAPI document](https://api.flowyn.dev/openapi/v1.yaml) lists every field. Response: ```json { "id": "1a6d8f2c-5b3e-4a9d-8c7f-6e0b2d4a1c95", "environment": "test", "name": "Nightly backup", "type": "heartbeat", "enabled": true, "paused": false, "current_status": "waiting", "interval_seconds": 86400 } ``` #### Lifecycle 1. Create the heartbeat with `type: heartbeat` and an empty `url`. 2. Your process calls `POST /v1/monitors/{id}/ping` with your API key at the end of each successful run. 3. If no ping arrives within twice the interval, Flowyn records a missed heartbeat. 4. Three consecutive misses open an incident; the next ping closes it. #### Status values | Status | Meaning | | --- | --- | | waiting | No result yet. | | healthy | The latest result passed. | | degraded | Results are mixed. | | failing | Recent results failed. | | paused | Not checking. | #### Retries Not applicable. #### Idempotency Pings are safe to repeat; each one is recorded as a result. #### Errors | Status | type | Meaning | | --- | --- | --- | | 400 | invalid_request | The body is not valid JSON, a field has the wrong type, or the body is over 1 MiB (`body_too_large`). | | 401 | unauthorized | Missing, malformed, or revoked API key. | | 402 | billing_error | The monthly credit allowance is used (`credit_limit_exceeded`). Reads keep working. | | 403 | forbidden | The key lacks the ability this call needs (`ability_required`). | | 404 | not_found | The id does not exist in this environment. | | 409 | conflict | For example `idempotency_mismatch`: the Idempotency-Key was used with a different body. | | 422 | invalid_request | A field value is out of range, for example `invalid_timeout`. | | 429 | rate_limited | Too many requests (`rate_limit_exceeded`). Wait for `Retry-After` seconds. | | 5xx | server_error | Flowyn failed. The body carries an `error_id` to quote to support. | #### Limits - `interval_seconds` 30–86,400 - A ping body may report `status` `success`, `failed`, or `down` with an optional `error` #### Read it back Keep the id and read the resource whenever you need its current state. bash: ```bash curl https://api.flowyn.dev/v1/monitors/1a6d8f2c-5b3e-4a9d-8c7f-6e0b2d4a1c95/results \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` #### Related - [HTTP monitors](https://flowyn.dev/docs/observe/monitors) - [Schedules](https://flowyn.dev/docs/execute/schedules) - [Heartbeats for cron jobs](https://flowyn.dev/docs/guides/heartbeat-monitoring-for-cron-jobs) #### Next step Assert on response bodies with [API checks](https://flowyn.dev/docs/observe/api-checks). ### API checks URL: https://flowyn.dev/docs/observe/api-checks Requests an API on an interval and evaluates assertions on the status code, headers, JSON body, text body, or response time. A failed assertion stores evidence. #### What api checks does Requests an API on an interval and evaluates assertions on the status code, headers, JSON body, text body, or response time. A failed assertion stores evidence. #### When to use api checks - Contract checks on APIs - Post-deploy verification - Partner API monitoring #### Authentication Send an API key as a bearer token. Keys are shown once, belong in a server environment, and are bound to one environment: `flw_test_` keys act in test and `flw_live_` keys in live. http: ```http Authorization: Bearer flw_test_•••• ``` #### Endpoint http: ```http POST https://api.flowyn.dev/v1/monitors ``` #### Request Send an `Idempotency-Key` so a retried request cannot create a duplicate. The body is JSON. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/monitors \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: check-orders-api" \ -d '{ "name": "Orders API", "type": "api_check", "method": "GET", "url": "https://api.example.com/orders/health", "interval_seconds": 300, "timeout_ms": 5000, "assertions": [ { "source": "status_code", "operator": "equals", "expected": 200 }, { "source": "json_body", "property": "$.status", "operator": "equals", "expected": "ok" } ] }' ``` #### Response Flowyn returns HTTP 201. The response is abridged here; the [OpenAPI document](https://api.flowyn.dev/openapi/v1.yaml) lists every field. Response: ```json { "id": "8e3c5a7b-1d9f-4e2a-b6c4-0a7f3d1e9b52", "environment": "test", "name": "Orders API", "type": "api_check", "current_status": "waiting", "interval_seconds": 300 } ``` #### Lifecycle 1. Create the check with its assertions. 2. Each run evaluates every assertion; all must pass. 3. A failed assertion stores evidence. Three consecutive failed results open an incident. 4. A passing run records recovery. #### Status values | Status | Meaning | | --- | --- | | waiting | No result yet. | | healthy | The latest result passed. | | degraded | Results are mixed. | | failing | Recent results failed. | | paused | Not checking. | #### Retries Not applicable; the next interval runs again. #### Idempotency Send `Idempotency-Key` on create. #### Errors | Status | type | Meaning | | --- | --- | --- | | 400 | invalid_request | The body is not valid JSON, a field has the wrong type, or the body is over 1 MiB (`body_too_large`). | | 401 | unauthorized | Missing, malformed, or revoked API key. | | 402 | billing_error | The monthly credit allowance is used (`credit_limit_exceeded`). Reads keep working. | | 403 | forbidden | The key lacks the ability this call needs (`ability_required`). | | 404 | not_found | The id does not exist in this environment. | | 409 | conflict | For example `idempotency_mismatch`: the Idempotency-Key was used with a different body. | | 422 | invalid_request | A field value is out of range, for example `invalid_timeout`. | | 429 | rate_limited | Too many requests (`rate_limit_exceeded`). Wait for `Retry-After` seconds. | | 5xx | server_error | Flowyn failed. The body carries an `error_id` to quote to support. | #### Limits - `interval_seconds` 30–86,400 - `timeout_ms` 100–30,000 - Up to 20 assertions #### Read it back Keep the id and read the resource whenever you need its current state. bash: ```bash curl https://api.flowyn.dev/v1/monitors/8e3c5a7b-1d9f-4e2a-b6c4-0a7f3d1e9b52/results \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` #### Related - [HTTP monitors](https://flowyn.dev/docs/observe/monitors) - [Issues](https://flowyn.dev/docs/observe/issues) #### Next step See how repeated failures become one [Issue](https://flowyn.dev/docs/observe/issues). ### Issues: grouped failure diagnostics URL: https://flowyn.dev/docs/observe/issues How Flowyn groups repeated failures from jobs, events, files, and monitors into issues with a count, first and last seen times, and a sample. #### What an issue is An issue is a group of stored failures that share a fingerprint: the same kind of resource, the same error code, and the same normalised error. It carries an occurrence count, first and last seen times, and a link to a sample failure. #### When to use issues - A webhook destination has been returning 500 for hours - A scheduled job fails the same way every night - A monitor keeps failing and you want one record, not hundreds #### How Flowyn groups failures 1. Every failed job, event delivery, file operation, and monitor result is stored with its error. 2. Flowyn normalises the error into a fingerprint. 3. Failures with the same fingerprint increment one issue and update `last_seen`. 4. The issue links to a sample failure with its full evidence. 5. Flowyn reports the issue. It does not change your system. #### Endpoints http: ```http GET https://api.flowyn.dev/v1/issues/{fingerprint} GET https://api.flowyn.dev/v1/issues/{fingerprint}/occurrences ``` The dashboard lists every open issue. #### Response Response: ```json { "fingerprint": "3f0a9c2e", "environment": "test", "kind": "event", "title": "Destination returned HTTP 500", "code": "destination_status", "count": 27, "first_seen": "2026-09-29T21:14:00Z", "last_seen": "2026-09-30T10:00:12Z", "sample_id": "0f8e4a52-3c1d-4e7b-9a26-5d1b7c3e9f10" } ``` #### Errors | Status | type | Meaning | | --- | --- | --- | | 400 | invalid_request | The body is not valid JSON, a field has the wrong type, or the body is over 1 MiB (`body_too_large`). | | 401 | unauthorized | Missing, malformed, or revoked API key. | | 402 | billing_error | The monthly credit allowance is used (`credit_limit_exceeded`). Reads keep working. | | 403 | forbidden | The key lacks the ability this call needs (`ability_required`). | | 404 | not_found | The id does not exist in this environment. | | 409 | conflict | For example `idempotency_mismatch`: the Idempotency-Key was used with a different body. | | 422 | invalid_request | A field value is out of range, for example `invalid_timeout`. | | 429 | rate_limited | Too many requests (`rate_limit_exceeded`). Wait for `Retry-After` seconds. | | 5xx | server_error | Flowyn failed. The body carries an `error_id` to quote to support. | #### Related - [Events and webhooks](https://flowyn.dev/docs/connect/events) - [Durable HTTP Jobs](https://flowyn.dev/docs/execute/jobs) - [HTTP monitors](https://flowyn.dev/docs/observe/monitors) #### Next step Change behaviour without a deploy: [Flags](https://flowyn.dev/docs/configure/flags). ## Configure · Flags ### Flag evaluation URL: https://flowyn.dev/docs/configure/flags Evaluates a flag for an identifier and attributes you send. Rules run in order and the first match wins; percentage conditions bucket the identifier deterministically. The response carries the value and the reason. #### What flag evaluation does Evaluates a flag for an identifier and attributes you send. Rules run in order and the first match wins; percentage conditions bucket the identifier deterministically. The response carries the value and the reason. #### When to use flag evaluation - Feature gating - Remote configuration for applications and agents - Per-plan behaviour #### Authentication Send an API key as a bearer token. Keys are shown once, belong in a server environment, and are bound to one environment: `flw_test_` keys act in test and `flw_live_` keys in live. http: ```http Authorization: Bearer flw_test_•••• ``` #### Endpoint http: ```http POST https://api.flowyn.dev/v1/flags/evaluate ``` #### Request The body is JSON. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/flags/evaluate \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "key": "checkout-v2", "identifier": "usr_142", "attributes": { "plan": "pro", "country": "DE" } }' ``` #### Response Flowyn returns HTTP 200. The response is abridged here; the [OpenAPI document](https://api.flowyn.dev/openapi/v1.yaml) lists every field. Response: ```json { "key": "checkout-v2", "environment": "test", "enabled": true, "value": true, "reason": "percentage_rollout", "matched_rule_id": "rule_rollout_25", "bucket": 1742, "configuration_version": 7, "algorithm_version": "v1" } ``` #### Lifecycle 1. Create the flag with a `key`, `value_type` (boolean, string, number, or json), a `default_value`, and optional rules. 2. Your application sends the key, an `identifier`, and `attributes`. 3. Rules are evaluated in order. The first rule whose conditions all match returns its value with `reason: targeting_match`. 4. A `percentage` condition hashes the identifier into a bucket from 0 to 9,999 and matches below the configured basis points (`reason: percentage_rollout`). 5. Otherwise the default is returned with `reason: default`. #### Status values | Status | Meaning | | --- | --- | | targeting_match | A rule matched; `matched_rule_id` names it. | | percentage_rollout | A percentage rule matched; `bucket` is returned. | | default | No rule matched. | | disabled | The flag is turned off in this environment. | | unconfigured | The flag has no value in this environment. | | archived | The flag is archived. | #### Retries Evaluation is synchronous and read-only. Retry freely. #### Idempotency Not needed. The same configuration version, identifier, and attributes always return the same value. #### Errors | Status | type | Meaning | | --- | --- | --- | | 400 | invalid_request | The body is not valid JSON, a field has the wrong type, or the body is over 1 MiB (`body_too_large`). | | 401 | unauthorized | Missing, malformed, or revoked API key. | | 402 | billing_error | The monthly credit allowance is used (`credit_limit_exceeded`). Reads keep working. | | 403 | forbidden | The key lacks the ability this call needs (`ability_required`). | | 404 | not_found | The id does not exist in this environment. | | 409 | conflict | For example `idempotency_mismatch`: the Idempotency-Key was used with a different body. | | 422 | invalid_request | A field value is out of range, for example `invalid_timeout`. | | 429 | rate_limited | Too many requests (`rate_limit_exceeded`). Wait for `Retry-After` seconds. | | 5xx | server_error | Flowyn failed. The body carries an `error_id` to quote to support. | #### Limits - Up to 20 attributes; string values up to 256 characters - Up to 20 rules per flag, 8 conditions per rule - Values up to 10 KB #### Create a flag bash: ```bash curl -X POST https://api.flowyn.dev/v1/flags \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: flag-checkout-v2" \ -d '{ "key": "checkout-v2", "name": "Checkout v2", "value_type": "boolean", "enabled": true, "default_value": false }' ``` #### Related - [Percentage rollouts](https://flowyn.dev/docs/configure/rollouts) #### Next step Read [Percentage rollouts](https://flowyn.dev/docs/configure/rollouts). ### Percentage rollouts URL: https://flowyn.dev/docs/configure/rollouts How Flowyn percentage rollouts bucket an identifier deterministically into 0–9,999 so the same user always gets the same result, and how to widen a rollout safely. #### How deterministic rollouts work Flowyn hashes the flag's stable seed, the environment, and the caller's `identifier` into a bucket from 0 to 9,999. A `percentage` condition with a value of 2,500 basis points (25%) matches buckets 0–2,499. The same identifier always lands in the same bucket, so a user included at 25% stays included at 50%. #### Configure a rollout rule Rule: ```json { "priority": 1, "conditions": [ { "operator": "percentage", "value": 2500 } ], "value": true } ``` #### Evaluate bash: ```bash curl -X POST https://api.flowyn.dev/v1/flags/evaluate \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "key": "checkout-v2", "identifier": "usr_142" }' ``` Response: ```json { "key": "checkout-v2", "value": true, "reason": "percentage_rollout", "bucket": 1742 } ``` #### Widening the rollout Raise the percentage from 2,500 to 5,000 basis points. Buckets 0–2,499 stay included and 2,500–4,999 are added. Nobody already included is removed. Setting it to 0 turns the rollout off for everyone not matched by an earlier rule. #### Variants For more than two outcomes, define variants with `allocation_basis_points` that add up to at most 10,000. The response names the `variant` it returned. #### Limits - Send a stable `identifier`; without one the identifier is empty and every caller shares a bucket - Percentages are whole basis points from 0 to 10,000 #### Related - [Flag evaluation](https://flowyn.dev/docs/configure/flags) #### Next step Back to the [Quickstart](https://flowyn.dev/docs/getting-started/quickstart) or the [REST reference](https://flowyn.dev/docs/tools/rest). ## Agents · Runs, traces & approvals ### Agent infrastructure: overview and quickstart URL: https://flowyn.dev/docs/agents/overview Register an agent you deploy yourself, record its runs, send traces, read its model usage, and ask a person to approve an action, all through the Flowyn REST API. Flowyn is the infrastructure around your agent, not the intelligence inside it. Your agent runs wherever you deploy it, with any framework and any model provider. Flowyn records its identity, its runs, its traces and steps, its model usage, and the decisions people make about it. #### What you need - A Flowyn API key for the test environment (`flw_test_…`) with the read and run abilities. Keep it in your agent's server environment. - An agent you already run: OpenAI Agents SDK, LangGraph, CrewAI, Mastra, PydanticAI, or your own Python or Node.js code. There is no plugin to install. #### The model | Resource | What it is | Endpoint | | --- | --- | --- | | Agent | A registered identity for one agent you deploy | `/v1/agents` | | Run | A record of one execution your runtime reports | `/v1/agent-runs` | | Trace and span | Telemetry your runtime sends; spans in a run-linked trace are the run's steps | `/v1/agent-telemetry` | | Model usage | Tokens, latency, and cost totalled from model steps | `/v1/agent-telemetry/usage` | | Approval | A decision a signed-in person records for a running run | `/v1/agent-approvals` | | Provider connection | An encrypted OpenAI key stored per environment | `/v1/provider-connections` | #### 1. Register the agent Give the agent a name and, optionally, the id you already use for it. Flowyn returns a stable agent id. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/agents \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: register-refund-agent" \ -d '{ "name": "refund-agent", "description": "Handles refund requests from support tickets.", "external_id": "refund-agent" }' ``` Response: ```json { "id": "7d0f6c1e-2b1a-4c55-9f53-1a2b3c4d5e6f", "name": "refund-agent", "description": "Handles refund requests from support tickets.", "external_id": "refund-agent", "environment": "test", "status": "active", "revision": 1, "created_at": "2026-09-30T09:58:00Z", "updated_at": "2026-09-30T09:58:00Z", "archived_at": null } ``` #### 2. Record that a run started Your agent is already running on your infrastructure. This call records that fact and returns a run id to attach telemetry and approvals to. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/agent-runs \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: run-ticket-1042" \ -d '{ "agent_id": "7d0f6c1e-2b1a-4c55-9f53-1a2b3c4d5e6f", "external_run_id": "ticket-1042" }' ``` Response: ```json { "id": "3b9d2f7a-8c41-4e0b-a5d6-0f1e2d3c4b5a", "agent_id": "7d0f6c1e-2b1a-4c55-9f53-1a2b3c4d5e6f", "external_run_id": "ticket-1042", "environment": "test", "status": "running", "error": null, "created_at": "2026-09-30T10:00:00Z", "finished_at": null, "updated_at": "2026-09-30T10:00:00Z" } ``` #### 3. Send traces The root span carries the run_id. Child spans of kind generation are model steps; kind tool are tool steps. Each item gets its own outcome. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/agent-telemetry \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "items": [ { "event_id": "0b8d6f1e-5a43-4c2e-9d11-7f3c2a1b0e01", "emitted_at": "2026-09-30T10:00:04Z", "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", "span_id": "00f067aa0ba902b7", "name": "handle-refund", "kind": "span", "started_at": "2026-09-30T10:00:00Z", "status": "running", "agent_id": "7d0f6c1e-2b1a-4c55-9f53-1a2b3c4d5e6f", "run_id": "3b9d2f7a-8c41-4e0b-a5d6-0f1e2d3c4b5a" }, { "event_id": "1c9e7a2f-6b54-4d3f-8e22-8a4d3b2c1f02", "emitted_at": "2026-09-30T10:00:04Z", "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", "span_id": "5c1a9e7d3b2f4a60", "parent_span_id": "00f067aa0ba902b7", "name": "classify-request", "kind": "generation", "started_at": "2026-09-30T10:00:00.2Z", "ended_at": "2026-09-30T10:00:01.1Z", "status": "succeeded", "provider": "openai", "model": "gpt-4.1-mini", "input_tokens": 812, "cached_input_tokens": 512, "output_tokens": 64 }, { "event_id": "2d0f8b3a-7c65-4e4a-9f33-9b5e4c3d2a03", "emitted_at": "2026-09-30T10:00:04Z", "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", "span_id": "9e2d4b6a8c0f1e35", "parent_span_id": "00f067aa0ba902b7", "name": "lookup_order", "kind": "tool", "tool_call_id": "call_7Hq2", "started_at": "2026-09-30T10:00:01.2Z", "ended_at": "2026-09-30T10:00:01.5Z", "status": "succeeded" } ] }' ``` Response: ```json { "items": [ { "event_id": "0b8d6f1e-5a43-4c2e-9d11-7f3c2a1b0e01", "outcome": "accepted", "agent_link": "linked", "run_link": "linked" }, { "event_id": "1c9e7a2f-6b54-4d3f-8e22-8a4d3b2c1f02", "outcome": "accepted" }, { "event_id": "2d0f8b3a-7c65-4e4a-9f33-9b5e4c3d2a03", "outcome": "accepted" } ] } ``` #### 4. Inspect model and tool steps Read the trace back as a flat list of steps with parent ids, durations, tokens, cost, and errors. The dashboard shows the same trace under Traces. cURL: ```bash curl https://api.flowyn.dev/v1/agent-telemetry/4bf92f3577b34da6a3ce929d0e0e4736 \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` #### 5. Understand model usage Read per-Agent usage through the trace root: child generations count even without agent_id; rootless or unlinked traces are excluded. Optional 24-hour thresholds warn on observed usage greater than or equal to the threshold, computed on request. Missing and unpriced contributions stay visible; other windows are analysis only. cURL: ```bash curl https://api.flowyn.dev/v1/agent-telemetry/usage?agent_id=7d0f6c1e-2b1a-4c55-9f53-1a2b3c4d5e6f&last=24h&group_by=model&bucket=hour \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` #### 6. Ask a person to decide Create an approval request for the running run. It is pending until a signed-in person approves or rejects it in the Flowyn dashboard. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/agent-approvals \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: approval-refund-1042" \ -d '{ "run_id": "3b9d2f7a-8c41-4e0b-a5d6-0f1e2d3c4b5a", "title": "Refund order 1042", "description": "Refund 19.99 USD to the original card.", "expires_in_seconds": 3600, "request_key": "refund-1042" }' ``` Response: ```json { "id": "5c1f7a52-0d2e-4b6a-8a4f-2b7c9d1e3f40", "run_id": "3b9d2f7a-8c41-4e0b-a5d6-0f1e2d3c4b5a", "agent_id": "7d0f6c1e-2b1a-4c55-9f53-1a2b3c4d5e6f", "agent_name": "refund-agent", "run_status": "running", "environment": "test", "title": "Refund order 1042", "description": "Refund 19.99 USD to the original card.", "request_key": "refund-1042", "status": "pending", "expires_at": "2026-09-30T11:00:00Z", "created_at": "2026-09-30T10:00:02Z", "decision": null } ``` #### 7. Continue according to your runtime Your agent polls the approval with backoff, reads approved or rejected, and decides what to do. Flowyn never blocks, pauses, or resumes it. cURL: ```bash curl https://api.flowyn.dev/v1/agent-approvals/5c1f7a52-0d2e-4b6a-8a4f-2b7c9d1e3f40 \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` Response (excerpt): ```json { "id": "5c1f7a52-0d2e-4b6a-8a4f-2b7c9d1e3f40", "status": "approved", "decision": { "value": "approved", "reason": "Matches the ticket.", "decided_at": "2026-09-30T10:03:10Z", "decided_by": null } } ``` #### 8. Report the outcome Report the run's one terminal outcome. The same outcome again is a no-op; a different one is rejected. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/agent-runs/3b9d2f7a-8c41-4e0b-a5d6-0f1e2d3c4b5a/finish \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: finish-ticket-1042" \ -d '{ "status": "succeeded" }' ``` #### 9. Inspect the result Read the run, its traces, and its approvals by id. Telemetry is kept for 30 days; the run record stays. cURL: ```bash curl https://api.flowyn.dev/v1/agent-runs/3b9d2f7a-8c41-4e0b-a5d6-0f1e2d3c4b5a \ -H "Authorization: Bearer $FLOWYN_API_KEY" curl https://api.flowyn.dev/v1/agent-telemetry?run_id=3b9d2f7a-8c41-4e0b-a5d6-0f1e2d3c4b5a \ -H "Authorization: Bearer $FLOWYN_API_KEY" curl https://api.flowyn.dev/v1/agent-approvals?run_id=3b9d2f7a-8c41-4e0b-a5d6-0f1e2d3c4b5a \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` #### In the dashboard Traces, Model usage, Approvals, and Provider connections have their own pages in the Flowyn dashboard. Approval decisions are made there, by a signed-in person. #### What Flowyn does not do - Host, deploy, or execute your agent - Run model inference or proxy your model calls - Store prompts, completions, tool arguments, or tool results - Provide agent memory or a vector database - Pause, resume, signal, or cancel a running agent - Enforce an approval inside your runtime #### Next step Read [Agents and runs](https://flowyn.dev/docs/agents/registry-and-runs), then [Traces and steps](https://flowyn.dev/docs/agents/telemetry). ### Agents and runs: identity and execution records URL: https://flowyn.dev/docs/agents/registry-and-runs Register each agent you deploy with a durable identity, then record every execution as a run that moves from running to succeeded, failed, or cancelled exactly once. #### Agents An agent is an identity: a `name`, an optional `description`, and an optional `external_id` you already use. Flowyn stores no code, prompts, or model policy for it. Optional rolling-24h warning thresholds are advisory configuration. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/agents \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: register-refund-agent" \ -d '{ "name": "refund-agent", "description": "Handles refund requests from support tickets.", "external_id": "refund-agent" }' ``` Response: ```json { "id": "7d0f6c1e-2b1a-4c55-9f53-1a2b3c4d5e6f", "name": "refund-agent", "description": "Handles refund requests from support tickets.", "external_id": "refund-agent", "environment": "test", "status": "active", "revision": 1, "created_at": "2026-09-30T09:58:00Z", "updated_at": "2026-09-30T09:58:00Z", "archived_at": null } ``` | Call | Effect | | --- | --- | | `POST /v1/agents` | Register. Returns 201. | | `GET /v1/agents` | List, filter by `q`, `external_id`, or `archived`. | | `PATCH /v1/agents/{id}` | Update metadata or warning thresholds. Send `expected_revision` and `Idempotency-Key`. | | `POST /v1/agents/{id}/archive` | Archive. The id stays for attribution. | | `POST /v1/agents/{id}/restore` | Restore if fewer than 100 agents are active. | - Names are unique among active agents in an environment; an `external_id` is unique among all of them. - Up to 100 active agents per environment. Archived agents do not count. - Changes use `expected_revision`, so two editors cannot overwrite each other (`409 agent_revision_conflict`). #### Runs A run records one execution your runtime performs. Flowyn does not run, queue, retry, or cancel it. Create it when work starts; it begins `running` with a server timestamp. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/agent-runs \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: run-ticket-1042" \ -d '{ "agent_id": "7d0f6c1e-2b1a-4c55-9f53-1a2b3c4d5e6f", "external_run_id": "ticket-1042" }' ``` Response: ```json { "id": "3b9d2f7a-8c41-4e0b-a5d6-0f1e2d3c4b5a", "agent_id": "7d0f6c1e-2b1a-4c55-9f53-1a2b3c4d5e6f", "external_run_id": "ticket-1042", "environment": "test", "status": "running", "error": null, "created_at": "2026-09-30T10:00:00Z", "finished_at": null, "updated_at": "2026-09-30T10:00:00Z" } ``` #### Finish a run Report the one terminal outcome. `error` is required for `failed` and rejected otherwise. Reporting the same outcome again returns the stored run unchanged; a different outcome is `409 agent_run_terminal`. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/agent-runs/3b9d2f7a-8c41-4e0b-a5d6-0f1e2d3c4b5a/finish \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: finish-ticket-1042" \ -d '{ "status": "failed", "error": { "code": "provider_error", "message": "Provider rejected the request." } }' ``` #### Status values | Status | Meaning | | --- | --- | | running | Your runtime reported that the run started. | | succeeded | Your runtime reported success. | | failed | Your runtime reported failure with a code and message. | | cancelled | Your runtime reported that it stopped the run. Flowyn did not cancel anything. | #### Rules worth knowing - The agent must be active in the same environment to start a run. A run whose agent is archived later can still finish. - `external_run_id` is optional, unique per agent, and immutable. Use your framework's thread or job id. - A live run needs an API key; a signed-in user can create runs in test only. - Send `Idempotency-Key` on create and finish so retries are safe. #### Read runs bash: ```bash curl https://api.flowyn.dev/v1/agent-runs/3b9d2f7a-8c41-4e0b-a5d6-0f1e2d3c4b5a \ -H "Authorization: Bearer $FLOWYN_API_KEY" curl https://api.flowyn.dev/v1/agent-runs?agent_id=7d0f6c1e-2b1a-4c55-9f53-1a2b3c4d5e6f&status=failed \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` Lists are newest first, at most 100 per page, with `next_cursor` for the next page. #### Next step Attach telemetry to the run: [Traces and steps](https://flowyn.dev/docs/agents/telemetry). ### Agent traces and steps for externally deployed agents URL: https://flowyn.dev/docs/agents/telemetry Send spans from wherever your agent runs. A trace linked to a run turns its spans into the run's steps: model steps, tool steps, and generic operations with hierarchy, timing, status, and errors. #### How telemetry is shaped A trace is one unit of agent work, identified by a 32-character lowercase hex `trace_id`. It contains spans, each with a 16-character `span_id` and an optional `parent_span_id`. These are the W3C Trace Context id formats, so ids your tracing already produces can be sent as they are. | kind | Step type | Typical fields | | --- | --- | --- | | span | Generic operation | `name`, `status`, timing | | generation | Model step | `provider`, `model`, `input_tokens`, `output_tokens`, `cached_input_tokens`, `reported_cost_usd` | | tool | Tool step | `name`, `tool_call_id` | #### Link a trace to a run Put `run_id` (and optionally `agent_id`) on the root span only. When the root is linked to a run in the same environment, every span in that trace is a step of the run. An id that does not match a run is stored unlinked rather than rejected. #### Send a batch `POST /v1/agent-telemetry` accepts 1 to 50 spans per request, up to 1 MiB. Each item gets its own outcome, so one bad span does not reject the others. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/agent-telemetry \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "items": [ { "event_id": "0b8d6f1e-5a43-4c2e-9d11-7f3c2a1b0e01", "emitted_at": "2026-09-30T10:00:04Z", "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", "span_id": "00f067aa0ba902b7", "name": "handle-refund", "kind": "span", "started_at": "2026-09-30T10:00:00Z", "status": "running", "agent_id": "7d0f6c1e-2b1a-4c55-9f53-1a2b3c4d5e6f", "run_id": "3b9d2f7a-8c41-4e0b-a5d6-0f1e2d3c4b5a" }, { "event_id": "1c9e7a2f-6b54-4d3f-8e22-8a4d3b2c1f02", "emitted_at": "2026-09-30T10:00:04Z", "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", "span_id": "5c1a9e7d3b2f4a60", "parent_span_id": "00f067aa0ba902b7", "name": "classify-request", "kind": "generation", "started_at": "2026-09-30T10:00:00.2Z", "ended_at": "2026-09-30T10:00:01.1Z", "status": "succeeded", "provider": "openai", "model": "gpt-4.1-mini", "input_tokens": 812, "cached_input_tokens": 512, "output_tokens": 64 }, { "event_id": "2d0f8b3a-7c65-4e4a-9f33-9b5e4c3d2a03", "emitted_at": "2026-09-30T10:00:04Z", "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", "span_id": "9e2d4b6a8c0f1e35", "parent_span_id": "00f067aa0ba902b7", "name": "lookup_order", "kind": "tool", "tool_call_id": "call_7Hq2", "started_at": "2026-09-30T10:00:01.2Z", "ended_at": "2026-09-30T10:00:01.5Z", "status": "succeeded" } ] }' ``` Response: ```json { "items": [ { "event_id": "0b8d6f1e-5a43-4c2e-9d11-7f3c2a1b0e01", "outcome": "accepted", "agent_link": "linked", "run_link": "linked" }, { "event_id": "1c9e7a2f-6b54-4d3f-8e22-8a4d3b2c1f02", "outcome": "accepted" }, { "event_id": "2d0f8b3a-7c65-4e4a-9f33-9b5e4c3d2a03", "outcome": "accepted" } ] } ``` | outcome | Meaning | | --- | --- | | accepted | A new span was stored, or a running span moved to its terminal status. | | duplicate | This `event_id` was already stored with identical content. | | ignored_stale | Nothing changed, for example a running snapshot after the span finished. | | conflict | Nothing changed; `code` says why, such as `span_conflict`. | | invalid | The item failed validation and was not stored; its `event_id` can be reused. | #### Span updates `event_id` makes each write idempotent. A span may be sent once as `running` and once more, under a new `event_id`, with its terminal status (`succeeded`, `failed`, or `cancelled`), `ended_at`, and any token fields not yet stored. Identity fields such as `name`, `kind`, and `parent_span_id` cannot change. #### What is not accepted Telemetry is metadata. Prompts, completions, tool arguments, tool results, headers, and URLs are not fields of the API, and a body with an unknown field is rejected. Error messages are limited to 240 characters. #### Read traces and steps bash: ```bash curl https://api.flowyn.dev/v1/agent-telemetry?run_id=3b9d2f7a-8c41-4e0b-a5d6-0f1e2d3c4b5a \ -H "Authorization: Bearer $FLOWYN_API_KEY" curl https://api.flowyn.dev/v1/agent-telemetry/4bf92f3577b34da6a3ce929d0e0e4736 \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` A trace reads back as a flat page of up to 200 spans ordered by start time, each with `duration_ms`, tokens, `cost`, and `error`. `incomplete` means a root or parent span is missing; `truncated` means the trace has more spans than the page. #### Span status and run status are independent A failed tool step does not fail the run, and finishing the run does not close open spans. Your runtime reports each one. #### Retention Telemetry, and therefore steps, are kept for 30 days. The run record stays. #### Next step [Model usage and cost](https://flowyn.dev/docs/agents/model-usage). ### Model usage and cost for agents URL: https://flowyn.dev/docs/agents/model-usage Read input, output, and cached tokens, latency, and cost for your agent's model calls: per step, per trace, and totalled by model, provider, service, or step name. #### Where usage comes from Usage comes from the generation spans your runtime sends: `provider`, `model`, `input_tokens`, `output_tokens`, `cached_input_tokens`, and optionally `reported_cost_usd`. Flowyn does not call your model provider to measure it. #### How cost is calculated | cost.status | Meaning | | --- | --- | | reported | You sent `reported_cost_usd`; Flowyn uses it. | | estimated | Calculated from the reported tokens and Flowyn's price catalog. | | pending | The span is still running. | | unavailable | Cannot be calculated; `reason` says why, such as `model_unknown`. | The price catalog covers selected OpenAI, Anthropic, and Google models, each price with the date it took effect. Unknown cost is never shown as zero. Reported costs are observed values; stored estimates retain their pricing provenance. Neither is a guaranteed provider invoice. #### Read usage bash: ```bash curl https://api.flowyn.dev/v1/agent-telemetry/usage?last=7d&group_by=model&bucket=day \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` | Parameter | Values | | --- | --- | | agent_id | Optional scoped Agent UUID; attribution follows the trace root | | last | `1h`, `24h` (default), `7d`, `14d`, `30d` | | group_by | `model`, `provider`, `service`, `name` (up to 50 groups) | | bucket | `hour` or `day`, at most 720 time-series points | The totals include generations, failed generations, input, cached input, output, and total tokens, generations missing usage, average and p95 duration, and cost split into reported and estimated amounts. #### Per-Agent advisory warnings Set `token_warning_24h` to a positive integer (at most 9,007,199,254,740,991) and `cost_warning_24h_usd` to a positive decimal string (at most 1,000,000 USD with at most 9 fractional digits) on Agent create or PATCH. Null disables the respective warning. PATCH requires the current `expected_revision` and an `Idempotency-Key`: an omitted threshold is preserved; explicit null clears it. cURL: ```bash curl -X PATCH https://api.flowyn.dev/v1/agents/7d0f6c1e-2b1a-4c55-9f53-1a2b3c4d5e6f \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: agent-warnings-001" \ -d '{ "expected_revision": 1, "token_warning_24h": 100000, "cost_warning_24h_usd": "2.50" }' ``` bash: ```bash curl https://api.flowyn.dev/v1/agent-telemetry/usage?agent_id=7d0f6c1e-2b1a-4c55-9f53-1a2b3c4d5e6f&last=24h&group_by=model&bucket=hour \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` Per-Agent attribution follows the scoped trace root. Child generation spans count even when they omit `agent_id`; rootless or unlinked traces are excluded and disclosed. The report uses the bounded interval `[from, evaluated_at)` and excludes future spans. Reported cost takes precedence over the stored estimate, while missing token usage and unpriced contributions remain visible. Warnings are computed only when requested, from the same view of data and configuration. The 24-hour response includes thresholds, Agent revision, `evaluated_at`, observed totals, and warning states. A warning is reached when observed usage is greater than or equal to its threshold. Other supported windows provide analysis only. This is observation and advice: no remaining balance, safe-to-spend amount, provider spending guarantee, or external-runtime enforcement. #### Per trace `GET /v1/agent-telemetry/{trace_id}` returns the same totals for one trace under `usage`, and each model step carries its own `cost` and `time_to_first_token_ms` when `completion_started_at` was sent. #### In the dashboard **Traces → Model usage** shows the same totals and groups. #### Next step [Human approvals](https://flowyn.dev/docs/agents/approvals). ### Human approvals for AI agents (human in the loop) URL: https://flowyn.dev/docs/agents/approvals Your agent asks a person to approve or reject one action. A signed-in person decides in the Flowyn dashboard. Your runtime reads the decision and decides how to continue. #### How approvals work 1. Your runtime creates an approval request for a **running** run. 2. A signed-in member of your workspace approves or rejects it in the dashboard, with an optional reason. 3. Your runtime polls the approval, reads the decision, and continues or stops in its own code. > Flowyn is the durable decision service. It does not block, pause, resume, cancel, or execute anything, and it cannot verify that your agent honoured the decision. `approved` means a person recorded an approval; it does not mean the action ran. #### Create a request cURL: ```bash curl -X POST https://api.flowyn.dev/v1/agent-approvals \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: approval-refund-1042" \ -d '{ "run_id": "3b9d2f7a-8c41-4e0b-a5d6-0f1e2d3c4b5a", "title": "Refund order 1042", "description": "Refund 19.99 USD to the original card.", "expires_in_seconds": 3600, "request_key": "refund-1042" }' ``` Response: ```json { "id": "5c1f7a52-0d2e-4b6a-8a4f-2b7c9d1e3f40", "run_id": "3b9d2f7a-8c41-4e0b-a5d6-0f1e2d3c4b5a", "agent_name": "refund-agent", "run_status": "running", "title": "Refund order 1042", "request_key": "refund-1042", "status": "pending", "expires_at": "2026-09-30T11:00:00Z", "decision": null } ``` - `title` (up to 80 characters) and `description` (up to 240) are plain text shown to the reviewer. - `expires_in_seconds` is 60 to 604,800 (7 days); the default is 24 hours. - `request_key` is optional and unique per run. Repeating it with the same content returns the existing approval with HTTP 200, so retries are safe. #### Wait for the decision Poll `GET /v1/agent-approvals/{id}` with backoff: start near 2 seconds, double up to about 30 seconds, add jitter, and stop at `expires_at`. Flowyn does not push or long-poll. bash: ```bash curl https://api.flowyn.dev/v1/agent-approvals/5c1f7a52-0d2e-4b6a-8a4f-2b7c9d1e3f40 \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` | status | Meaning | | --- | --- | | pending | Waiting for a person. | | approved | A person recorded an approval. | | rejected | A person recorded a rejection. | | expired | Nobody decided before `expires_at`. | #### Who can decide Only a signed-in person with the member, admin, or owner role, through the dashboard or a user session. An API key can never decide. A live decision also needs a verified email. The first decision is final; a different later decision is `409 approval_decided`. People see the reviewer's name and email; API keys see `decided_by: null`. #### Acting on the decision Use the approval id as the idempotency key for whatever your agent does next, because Flowyn cannot know whether or how often it acted. #### Limits - Up to 10 pending requests per run and 500 pending requests per environment - The run must be `running` when the request is created #### Next step [Provider connections](https://flowyn.dev/docs/agents/provider-connections). ### Provider connections: store an OpenAI API key encrypted URL: https://flowyn.dev/docs/agents/provider-connections Store an OpenAI API key encrypted on Flowyn's servers, separately for test and live, and test whether OpenAI accepts it. Using it is optional. #### What a provider connection is A named, encrypted model provider credential for one environment. OpenAI is the supported provider today. Your agent does not have to use it: it can keep calling OpenAI, or any other provider, with its own keys, and still send telemetry to Flowyn. #### Store a key The key is write-only. No response, log, or event ever contains it; responses show only its last four characters. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/provider-connections \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: openai-production" \ -d '{ "provider": "openai", "name": "Production OpenAI", "credential": "sk-••••" }' ``` Response: ```json { "id": "4e7a2c9f-1b3d-4f5e-8a6c-2d9b0e1f3a74", "provider": "openai", "name": "Production OpenAI", "environment": "test", "hint": "x9Qa", "secret_version": 1, "verification": { "state": "unverified", "reason": "", "checked_at": null } } ``` #### Test it `POST /v1/provider-connections/{id}/test` makes one request to OpenAI's models list with a 5-second timeout and no retries, at most once every 30 seconds per connection. | outcome | Meaning | | --- | --- | | accepted | OpenAI accepted the key at `checked_at`. | | rejected | OpenAI answered 401. | | unverified | Anything else, such as a timeout. It proves nothing about the key. | #### Manage | Call | Effect | | --- | --- | | `GET /v1/provider-connections` | List connections in the environment. | | `PATCH /v1/provider-connections/{id}` | Rename. | | `PUT /v1/provider-connections/{id}/credential` | Replace the key; optional `expected_version`. | | `DELETE /v1/provider-connections/{id}` | Delete. | #### Permissions Storing, replacing, renaming, or deleting a key needs an API key with the reveal ability, or the admin role for a person. Testing needs the run ability. Reading needs read. #### Next step Back to the [agent overview](https://flowyn.dev/docs/agents/overview), or read the [REST reference](https://flowyn.dev/docs/tools/rest). ## Tools & reference ### REST API reference URL: https://flowyn.dev/docs/tools/rest Base URL, headers, pagination, and the main Flowyn endpoints. The OpenAPI document is the complete, machine-readable contract. #### Base URL and headers http: ```http https://api.flowyn.dev Authorization: Bearer Content-Type: application/json Idempotency-Key: # on creates ``` #### OpenAPI The OpenAPI 3 document at [https://api.flowyn.dev/openapi/v1.yaml](https://api.flowyn.dev/openapi/v1.yaml) is the authoritative description of every request, response, and error. Point your client generator or coding agent at it rather than at this page. #### Main endpoints | Method | Path | Purpose | | --- | --- | --- | | POST | /v1/files | Upload a file (tus) | | GET | /v1/files/{id}/download | Download a file | | POST | /v1/pdf/parse | Parse a PDF | | POST | /v1/docx/parse | Parse a DOCX | | POST | /v1/pdf/from-html | Render an HTML file to PDF | | POST | /v1/split-pdf | Split a mixed PDF | | POST | /v1/pdf/split | Split a PDF by page ranges | | GET | /v1/file-jobs/{id} | Read a file job and its outputs | | POST | /v1/jobs | Create a durable HTTP job | | GET | /v1/jobs/{id} | Read a job | | GET | /v1/jobs/{id}/attempts | Read a job's attempts | | POST | /v1/jobs/{id}/cancel | Cancel a job | | POST | /v1/schedules | Create a schedule | | GET | /v1/schedules/{id}/occurrences | Read a schedule's occurrences | | POST | /v1/events | Send an event | | GET | /v1/events/{id}/attempts | Read delivery attempts | | POST | /v1/events/{id}/replay | Replay an event | | POST | /v1/endpoints | Create a webhook endpoint | | POST | /v1/endpoints/{id}/test | Send a test delivery | | POST | /v1/monitors | Create an HTTP, heartbeat, or API-check monitor | | POST | /v1/monitors/{id}/ping | Record a heartbeat ping | | GET | /v1/monitors/{id}/results | Read monitor results | | GET | /v1/monitors/{id}/incidents | Read monitor incidents | | GET | /v1/issues/{fingerprint} | Read an issue | | POST | /v1/flags | Create a flag | | POST | /v1/flags/evaluate | Evaluate a flag | | POST | /v1/agents | Register an agent | | POST | /v1/agent-runs | Record that an agent run started | | POST | /v1/agent-runs/{id}/finish | Report a run's outcome | | POST | /v1/agent-telemetry | Send agent spans | | GET | /v1/agent-telemetry/{trace_id} | Read a trace and its steps | | GET | /v1/agent-telemetry/usage | Read model usage and cost | | POST | /v1/agent-approvals | Ask a person to decide | | GET | /v1/agent-approvals/{id} | Read an approval decision | | POST | /v1/provider-connections | Store an encrypted provider key | | POST | /v1/provider-connections/{id}/test | Test a provider key | #### Pagination List endpoints accept `limit` and `cursor`. A response includes `next_cursor`; `null` means the last page. #### Environments An API key acts in its own environment. A signed-in session selects one with `?environment=test` or `live`. #### Next step [Coding agents: OpenAPI, llms.txt, SDK, CLI, MCP](https://flowyn.dev/docs/tools/agents). ### Using Flowyn from coding agents URL: https://flowyn.dev/docs/tools/agents How ChatGPT, Claude Code, Cursor, and other coding agents can discover and integrate Flowyn through REST, the OpenAPI document, llms.txt, and the repository SDK, CLI, and stdio MCP server. #### Can a coding agent integrate Flowyn? Yes. Flowyn is plain REST with JSON resources. An agent can read [llms.txt](https://flowyn.dev/llms.txt) for a summary, [llms-full.txt](https://flowyn.dev/llms-full.txt) for every docs page as text, and the [OpenAPI document](https://api.flowyn.dev/openapi/v1.yaml) for exact contracts. #### What an agent should know - Authenticate with `Authorization: Bearer` and keep the key server-side. - Send `Idempotency-Key` on creates. - Asynchronous creates return HTTP 202 with an id. Read the resource for status. - Delivery and execution are at-least-once. Deduplicate in receivers. - Flowyn records and reports. It does not operate the caller's systems or run their agents. #### A prompt you can paste text: ```text Integrate Flowyn (https://flowyn.dev). Read https://flowyn.dev/llms.txt and the OpenAPI document at https://api.flowyn.dev/openapi/v1.yaml. Use the REST API at https://api.flowyn.dev with Authorization: Bearer $FLOWYN_API_KEY from the server environment. Send an Idempotency-Key header on creates, keep the returned id, and read status with GET. Task: . ``` #### Instrumenting an agent you built If the agent you are building should report to Flowyn, start with the [agent overview](https://flowyn.dev/docs/agents/overview). Use REST or the repository Node SDK resources `agents`, `agentRuns`, `telemetry`, `approvals`, and `providerConnections`. Approval decisions remain available only through a human session. #### SDK, CLI, and MCP A Node SDK, a CLI, and a stdio MCP server live in the Flowyn repository. The Node SDK wraps existing Agent APIs. MCP also provides read-only Agent identity, run, trace, usage, and approval inspection. Use SDK or REST for other Agent operations; the CLI retains its existing primitive commands. These packages are not published to a registry and the MCP server is not hosted. #### Build from source From the Flowyn repository root, use Node >= 22.13 and pnpm 11.21.0: bash: ```bash pnpm install --frozen-lockfile pnpm --filter @flowyn/cli build # Set FLOWYN_API_KEY securely in your server environment first. pnpm flowyn context pnpm flowyn --help ``` #### Stdio MCP configuration Configure your coding client to launch Node with the absolute path to `packages/cli/dist/index.js`, arguments `mcp --preset read`, and a server-side Development key. Set `FLOWYN_BASE_URL` only when using a different API deployment. Stdio is the connection; there is no hosted MCP URL. Read tools are the default. `--preset operate` enables creates and retries while omitting deletion and secret rotation; cancellation requires `confirm_resource_name` equal to the resource id; `--write` enables the full existing mutation set with explicit destructive confirmation. Production writes also require `--allow-live`. #### Codex Add this to `~/.codex/config.toml`. Set `FLOWYN_API_KEY` securely in the environment that launches Codex; `env_vars` passes the named variable to the server without storing its value in the configuration. Verify with `codex mcp list`. See the [Codex MCP reference](https://learn.chatgpt.com/docs/extend/mcp?surface=cli). toml: ```bash [mcp_servers.flowyn] command = "node" args = ["/absolute/path/to/flowyn/packages/cli/dist/index.js", "mcp", "--preset", "read"] env_vars = ["FLOWYN_API_KEY"] ``` #### Claude Code and Cursor Claude Code uses project `.mcp.json`. Cursor uses project `.cursor/mcp.json` or user `~/.cursor/mcp.json`. Store your `flw_test_` key in a mode-600 file outside the repository. This configuration supplies only the absolute path; Flowyn reads the key file when the client launches the server. Claude Code / Cursor configuration: ```json { "mcpServers": { "flowyn": { "command": "node", "args": [ "/absolute/path/to/flowyn/packages/cli/dist/index.js", "mcp", "--preset", "read" ], "env": { "FLOWYN_API_KEY_FILE": "/absolute/path/outside/repository/flowyn-test-key" } } } } ``` #### Discovery Call `session_context`, then `discover_capabilities`. The catalogue covers all five primitives and Agent infrastructure. Use REST when a capability has no SDK, CLI, or MCP name; do not invent a tool from a route. #### Inspect an Agent Use `agents_list` and `agents_get` to resolve an identity, then `agent_runs_list` and `agent_runs_get` to find its reported runs. Pass a returned run id to `agent_traces_list`, then its `trace_id` to `agent_traces_get`. Trace steps are concurrent metadata, retained 30 days; a `truncated` trace is incomplete evidence. `agent_usage` with `agent_id` and `last: "24h"` reads advisory warnings, which never enforce a budget or restrict execution. Use `agent_approvals_list` with `status: "pending"` and `agent_approvals_get` to explain the action a signed-in human must decide. MCP cannot decide, pause, resume, signal, or cancel an Agent. #### Uncertain writes The SDK reuses an idempotency key within its retries. A final `FlowynError` preserves it as `idempotencyKey`. Retry the same body with `{ idempotencyKey: error.idempotencyKey }`; a new SDK call without that option would create a new key. Preserve `next`, `retryAfterSeconds`, and request/error ids when handling errors. The canonical reference is https://flowyn.dev/docs/getting-started/errors. #### Next step [Limits and credits](https://flowyn.dev/docs/tools/limits). ### Limits, credits, and what an operation costs URL: https://flowyn.dev/docs/tools/limits Flowyn plan allowances in credits, how operations consume them, what happens when the allowance is used, and the main per-product limits. #### Plan allowances | Plan | Price | Credits a month | History | | --- | --- | --- | --- | | Starter | $0 | 10,000 | 3 days | | Production | $9/month | 200,000 | 14 days | | Capacity | $39/month | 1,000,000 | 30 days | #### What an operation costs Durable HTTP executions, event deliveries, scheduled executions, and monitor checks use the lightweight class. Automatic retries are included. Reading resources with GET does not consume credits. | Class | Credits | Examples | | --- | --- | --- | | Lightweight infrastructure | 1 | HTTP jobs, event delivery, schedules, monitors, heartbeats | | Standard document | 5 | PDF merge, split, extract, compression | | Parse or conversion | 10 | PDF and DOCX parse, HTML and DOCX to PDF | | Intelligent | 20 | Intelligent PDF split | #### What happens at the allowance New billable creates return HTTP 402 with `credit_limit_exceeded`, the usage, the limit, and the reset time until the next reset or an upgrade. There is no automatic overage. Existing resources, attempts, and history remain readable. #### Per-product limits | Product | Limit | | --- | --- | | Requests | JSON bodies up to 1 MiB | | Documents | 50 MB per file, 200 pages per operation, no OCR, up to 3 attempts | | HTTP jobs and schedules | Timeout 100–30,000 ms, 1–20 attempts, body up to 65,536 characters | | Events | Up to 8 delivery attempts, HTTPS endpoints, at most 50 endpoints per event | | Monitors | Interval 30 s to 24 h, up to 20 assertions | | Flags | Up to 20 attributes, 20 rules per flag, values up to 10 KB | | Agents | 100 active agents per environment, 50 spans per telemetry batch, 30-day telemetry retention | | Approvals | 10 pending per run, 500 pending per environment, expiry up to 7 days | #### Next step See [Pricing](https://flowyn.dev/pricing) or start with the [Quickstart](https://flowyn.dev/docs/getting-started/quickstart). ## Guides ### How to implement reliable webhooks and webhook retries URL: https://flowyn.dev/docs/guides/how-webhook-retries-work What makes a webhook unreliable, how retries with backoff work, why receivers must deduplicate, and how to implement reliable webhook delivery with Flowyn events. #### Why a plain webhook is unreliable A webhook is one HTTP POST from your system to somebody else's. It fails when the receiver is down, slow, rate-limited, or returns 500. If the POST runs inside your request handler, the failure is lost with the request, and nothing records that it happened. #### What reliable webhook delivery requires - **Store first.** Persist the event before trying to deliver it, so nothing is lost if delivery fails. - **Sign every delivery.** Receivers must be able to verify that the payload came from you. - **Retry with backoff.** Wait longer after each failure instead of retrying in a tight loop. - **Record every attempt.** Status code, latency, and timestamp, appended and never overwritten. - **Replay on demand.** Resend a past event after a receiver bug is fixed. - **Group repeated failures.** One issue for “destination returns 500”, not hundreds of alerts. #### How webhook retries work in Flowyn 1. `POST /v1/events` stores the event and returns HTTP 202. Your request is finished. 2. Flowyn signs the payload with the endpoint secret and POSTs it. The response is attempt 1. 3. A failure records the attempt and schedules the next one. 4. Up to 8 attempts are made over roughly a day and a half. 5. A 2xx marks the delivery `delivered`. Failed deliveries stay readable and can be replayed. | Attempt | Wait before (approx.) | Cumulative (approx.) | | --- | --- | --- | | 1 | immediate | 0 | | 2 | 5 s | 5 s | | 3 | 5 min | 5 min | | 4 | 30 min | 35 min | | 5 | 2 h | 2 h 35 min | | 6 | 5 h | 7 h 35 min | | 7 | 10 h | 17 h 35 min | | 8 | 10 h | 27 h 35 min | #### Why receivers must deduplicate Delivery is at-least-once. A receiver can process a request and then fail to return a response; Flowyn sees a failure and retries, and the receiver sees the event twice. Store the `webhook-id` header, which stays the same across retries, and ignore repeats. #### Implement it Create the event with an idempotency key so a retried create cannot duplicate it. cURL: ```bash curl -X POST https://api.flowyn.dev/v1/events \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order-123-completed" \ -d '{ "type": "order.completed", "payload": { "order_id": "ord_123" } }' ``` Response: ```json { "id": "0f8e4a52-3c1d-4e7b-9a26-5d1b7c3e9f10", "type": "order.completed", "status": "accepted", "attempt_count": 0 } ``` Read the attempts later, or replay after a fix: bash: ```bash curl https://api.flowyn.dev/v1/events/0f8e4a52-3c1d-4e7b-9a26-5d1b7c3e9f10/attempts \ -H "Authorization: Bearer $FLOWYN_API_KEY" curl -X POST https://api.flowyn.dev/v1/events/0f8e4a52-3c1d-4e7b-9a26-5d1b7c3e9f10/replay \ -H "Authorization: Bearer $FLOWYN_API_KEY" ``` #### Webhook retry best practices - Return 2xx quickly, then process asynchronously in the receiver. - Verify the Standard Webhooks signature and reject old timestamps. - Keep attempt history visible to the people who debug integrations. - Watch the receiver with an [HTTP monitor](https://flowyn.dev/docs/observe/monitors) so an outage is an incident, not a surprise. #### Related - [Events and webhooks reference](https://flowyn.dev/docs/connect/events) - [Idempotency and retries](https://flowyn.dev/docs/getting-started/idempotency) - [Issues: grouped failure diagnostics](https://flowyn.dev/docs/observe/issues) #### Next step Try a delivery with a failing first attempt in the [Playground](https://flowyn.dev/playground?op=connect&run=1). ### How to design idempotent API operations URL: https://flowyn.dev/docs/guides/designing-idempotent-api-operations What idempotency means for create requests and outbound calls, how idempotency keys work, how to choose a key, and how to build receivers that tolerate at-least-once delivery. #### What idempotent means An operation is idempotent when performing it twice has the same effect as performing it once. Retries become safe: a client that lost the response can send the request again without creating a duplicate order, payment, or email. #### Where duplicates come from - A client times out after the server committed the write and retries. - A durable job or webhook delivery is attempted twice because the first response was lost. - A user double-clicks, or a deploy replays a queue. #### Idempotency keys on create requests The client chooses a key that identifies the intent, such as `order-123-completed`, and sends it as `Idempotency-Key`. The server stores the key with the result. A repeat with the same key and body returns the stored result; a repeat with a different body is a conflict. http: ```http Idempotency-Key: order-123-completed ``` | Situation | Flowyn response | | --- | --- | | First request | Creates the resource | | Same key, same body (within 24 h) | Returns the original response, no duplicate | | Same key, different body | 409 `idempotency_mismatch` | | Different key | Creates a new resource | #### Choosing a good key - Derive it from the business event: `invoice-881-sync`, `user-42-welcome-email`. - Do not generate a random UUID per attempt; that defeats the purpose. - Keep it stable across retries and across processes. #### Idempotency on outbound calls Flowyn sends a stable id on every outbound attempt: `Idempotency-Key: ` on HTTP jobs and `webhook-id: ` on webhook deliveries. Receivers should store processed ids and return 2xx for repeats without redoing the work. js: ```js // receiver const id = req.headers["webhook-id"]; if (await seen(id)) return res.status(200).end(); await process(req.body); await markSeen(id); res.status(200).end(); ``` #### Designing the operation itself - Prefer upserts keyed by a natural id over blind inserts. - Make state transitions explicit: `pending → paid` is safe to apply twice; `balance += 10` is not. - Return the current resource on repeat, not an error, so clients can resume. #### Related - [Idempotency and retries](https://flowyn.dev/docs/getting-started/idempotency) - [Durable HTTP Jobs](https://flowyn.dev/docs/execute/jobs) - [Reliable webhooks guide](https://flowyn.dev/docs/guides/how-webhook-retries-work) #### Next step Create a job with an idempotency key in the [Playground](https://flowyn.dev/playground?op=execute&run=1). ### Heartbeat monitoring for cron jobs URL: https://flowyn.dev/docs/guides/heartbeat-monitoring-for-cron-jobs How to detect a cron job or worker that silently stopped with heartbeat (dead man's switch) monitoring: interval, pings, missed heartbeats, incidents, and recovery. #### The problem with cron A cron job that stops running produces no error. The server rebooted, the crontab was lost in a deploy, the container never started, or the job hangs. Nothing alerts you because nothing failed loudly, and uptime monitoring cannot see it: there is no URL to poll. #### What a heartbeat monitor does You tell Flowyn how often the job runs (`interval_seconds`). The job pings Flowyn at the end of every successful run. If no ping arrives within twice the interval, Flowyn records a missed heartbeat; three consecutive misses open an incident. The next ping closes it. Every ping is recorded. #### Create the heartbeat cURL: ```bash curl -X POST https://api.flowyn.dev/v1/monitors \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: hb-nightly-backup" \ -d '{ "name": "Nightly backup", "type": "heartbeat", "method": "GET", "url": "", "interval_seconds": 86400, "timeout_ms": 1000, "assertions": [] }' ``` Response: ```json { "id": "1a6d8f2c-5b3e-4a9d-8c7f-6e0b2d4a1c95", "type": "heartbeat", "current_status": "waiting", "interval_seconds": 86400 } ``` #### Ping from the job Put the ping at the end of the job so a crash before completion shows up as a missed heartbeat. The ping needs your API key, so keep it in the job's environment. bash: ```bash #!/bin/sh set -e pg_dump mydb | gzip > /backups/$(date +%F).sql.gz curl -fsS -X POST https://api.flowyn.dev/v1/monitors/1a6d8f2c-5b3e-4a9d-8c7f-6e0b2d4a1c95/ping \ -H "Authorization: Bearer $FLOWYN_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` A job that knows it failed can say so with `{"status":"failed","error":"disk full"}` instead of staying silent. #### Choosing the interval | Job | interval_seconds | | --- | --- | | Every-5-minutes worker | 300 | | Hourly sync | 3600 | | Nightly backup | 86400 | Set the interval to how often the job should run. The twofold window absorbs normal jitter; a job that runs less often than daily needs another signal, because the maximum interval is 24 hours. #### What you read back - `current_status` of the monitor - Results with `GET /v1/monitors/{id}/results` - Incidents with `GET /v1/monitors/{id}/incidents` #### Combine with schedules If the cron itself is the weak part, move it to a [Flowyn schedule](https://flowyn.dev/docs/execute/schedules). Each occurrence becomes a durable HTTP job with attempts, and missed occurrences are skipped rather than backfilled. Keep the heartbeat as an independent check. #### Related - [Heartbeats reference](https://flowyn.dev/docs/observe/heartbeats) - [HTTP monitors](https://flowyn.dev/docs/observe/monitors) - [Schedules](https://flowyn.dev/docs/execute/schedules) #### Next step Read how Flowyn groups repeated failures into [Issues](https://flowyn.dev/docs/observe/issues).