Skip to content

Published APIs

Published APIs let you expose a flow as an inbound API endpoint. External consumers call your endpoint, data flows through your pipeline, and results are optionally returned synchronously.

A published API endpoint is defined with:

source:
connector: api-gateway
trigger: api

The full published API configuration is managed via the web UI, API, or MCP tools.

FieldDescription
pathAPI path (e.g., "partner-orders")
methodHTTP method: GET, POST, PUT, PATCH, DELETE
requestSchemaJSON Schema for request validation
responseSchemaJSON Schema for response (enables synchronous mode)
responseMappingMapping block for response transformation
authTypeapi_key, oauth2, or none
validationModestrict (reject invalid) or lenient (log and continue)

{
"name": "create_published_api",
"arguments": {
"description": "Inbound endpoint for partner order submissions with validation",
"example_payload": {
"partner_id": "ACME",
"order_id": "PO-2024-001",
"items": [{"sku": "WIDGET-A", "qty": 10}]
}
}
}

The AI generates the API configuration including request schema, response mapping, and validation rules.


Each published API can have multiple consumer keys for authentication and tracking:

Consumer keys are stored in the consumer_api_keys table. Keys are SHA-256 hashed at rest — fyrn never stores the raw key after initial creation. Each key has a human-readable prefix (e.g., fyrn_ck_acme_) so you can identify the consumer from logs without exposing the secret.

Terminal window
fyrn api keys create \
--api partner-orders \
--consumer "ACME Corp" \
--rate-limit 50/s \
--daily-limit 100000

The CLI returns the full key once. Store it securely — it cannot be retrieved again.

Consumer key created:
Key: fyrn_ck_acme_sk_a1b2c3d4e5f6...
Consumer: ACME Corp
Rate limit: 50 req/s, 100,000 req/day
Status: active

Rotation creates a new key and deactivates the old one. You can set a grace period during which both keys are accepted:

Terminal window
fyrn api keys rotate \
--api partner-orders \
--consumer "ACME Corp" \
--grace-period 24h

During the grace period, both the old and new keys are valid. After it expires, the old key returns HTTP 401.

Each key tracks requestCount and lastUsedAt. When a consumer exceeds their per-second or daily limit, the API returns HTTP 429 with a Retry-After header.

StateBehavior
activeAccepts requests, counts toward limits
inactiveRejects with HTTP 401 — used during rotation grace periods or manual suspension
  • Create: Generate a new consumer key for a partner
  • Rotate: Create a new key and deactivate the old one
  • Rate limit: Each key can have per-second and daily limits
  • Track usage: lastUsedAt and requestCount per key

Invalid requests are rejected with HTTP 422:

{
"error": "Validation failed",
"details": [
{"field": "items[0].qty", "message": "must be a positive integer"}
]
}

Invalid requests are logged but processed. Useful during onboarding when partners are still aligning to the schema.


Published APIs support versioned endpoints so you can evolve schemas without breaking existing consumers.

Consumers specify the version via path prefix:

POST https://api.fyrn.ai/v1/partner-orders
POST https://api.fyrn.ai/v2/partner-orders

Requests without a version prefix are routed to the latest active version.

Terminal window
fyrn api versions create \
--api partner-orders \
--version v2 \
--schema ./schemas/partner-orders-v2.json

This creates a new version in active state. The previous version remains active until you explicitly deprecate it.

Terminal window
fyrn api versions deprecate \
--api partner-orders \
--version v1 \
--sunset 2026-06-01

Deprecated versions continue to serve requests but include a Sunset header and a Deprecation header in every response. Consumers see these in their HTTP responses:

Sunset: Sat, 01 Jun 2026 00:00:00 GMT
Deprecation: true

Once the sunset date passes, retire the version to stop serving requests:

Terminal window
fyrn api versions retire \
--api partner-orders \
--version v1

Retired versions return HTTP 410 Gone with a body pointing consumers to the current active version.

StatusDescription
activeCurrent version, accepting requests
deprecatedStill works but sunset date is set
retiredNo longer accepts requests

{
"name": "list_published_apis",
"arguments": {}
}
{
"name": "call_published_api",
"arguments": {
"api_path": "partner-orders",
"payload": {"partner_id": "ACME", "order_id": "PO-001"}
}
}

This example creates an inbound API that accepts partner purchase orders, validates the payload, creates an order in your ERP, and returns an acknowledgment with the internal order ID.

flows/partner-orders.yml
name: partner-order-ingest
description: Accept partner orders via API and create in ERP
source:
connector: api-gateway
trigger: api
steps:
- name: validate-partner
action: lookup
connector: postgres
params:
table: partners
match:
partner_id: "{{ source.partner_id }}"
on_failure: reject
- name: create-order
action: create
connector: erp
params:
object: SalesOrder
mapping:
external_ref: "{{ source.order_id }}"
partner_id: "{{ source.partner_id }}"
line_items: "{{ source.items }}"
- name: build-response
action: transform
params:
output:
status: accepted
internal_order_id: "{{ steps.create-order.id }}"
received_at: "{{ now() }}"
{
"name": "create_published_api",
"arguments": {
"flowName": "partner-order-ingest",
"path": "partner-orders",
"method": "POST",
"authType": "api_key",
"validationMode": "strict",
"requestSchema": {
"type": "object",
"required": ["partner_id", "order_id", "items"],
"properties": {
"partner_id": { "type": "string" },
"order_id": { "type": "string", "pattern": "^PO-\\d{4}-\\d{3,}$" },
"items": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": ["sku", "qty"],
"properties": {
"sku": { "type": "string" },
"qty": { "type": "integer", "minimum": 1 }
}
}
}
}
},
"responseMapping": "steps.build-response.output"
}
}
Terminal window
curl -X POST https://api.fyrn.ai/v1/partner-orders \
-H "Authorization: Bearer fyrn_ck_acme_sk_a1b2c3d4..." \
-H "Content-Type: application/json" \
-d '{
"partner_id": "ACME",
"order_id": "PO-2024-001",
"items": [
{"sku": "WIDGET-A", "qty": 10},
{"sku": "WIDGET-B", "qty": 5}
]
}'
{
"status": "accepted",
"internal_order_id": "SO-88291",
"received_at": "2026-03-05T14:22:08Z"
}

If validation fails (e.g., missing items), the API returns HTTP 422 before the flow executes, so no partial processing occurs.