From 5359654676f58d293977d50e95b8f351f6e3edf2 Mon Sep 17 00:00:00 2001 From: Rakshith Ramprakash Date: Fri, 25 Sep 2026 18:02:44 +1000 Subject: [PATCH 1/5] docs(agent): document exchange.onTermsRequired and the terms-required flow Agent runs only call Alexandria providers whose data terms the team has accepted. Document the skip / ask / fail modes, the skippedProviders, requiresAction and error response fields, and the accept-then-continue flow (terms/show, explicit user consent, terms/accept, then exchange.approve on the thread). There is no auto-accept mode. Co-Authored-By: Claude Opus 5.5 --- features/agent.mdx | 113 ++++++++++++++++++++++++++++++++++++++++ features/alexandria.mdx | 2 +- 2 files changed, 114 insertions(+), 1 deletion(-) diff --git a/features/agent.mdx b/features/agent.mdx index 7fc68b158..afb9f6321 100644 --- a/features/agent.mdx +++ b/features/agent.mdx @@ -248,6 +248,118 @@ The `model` parameter is optional — every request runs `spark-2`: +## Data providers that need terms + +Agent can call [Alexandria](/features/alexandria) data providers during a run, but only the ones whose data terms your team has accepted. A provider whose terms you haven't accepted is never called, whatever `exchange.onTermsRequired` is set to. The run carries on with the providers it can use, and the status response reports what it skipped. + +There is no auto-accept mode. Accepting a provider's terms always needs a person to agree to them, either in the [dashboard](https://www.firecrawl.dev/app/settings?tab=data-sources) or through a `terms/accept` call your application makes after its user has explicitly agreed. If you build an agent on top of Firecrawl, it must ask its user before calling `terms/accept`. A request for data isn't consent to a provider's terms. + +| `onTermsRequired` | What happens | +|---|---| +| `skip` (default) | The run answers with accepted providers only and never blocks. `exchange.skippedProviders` lists the gated providers that would have helped. | +| `ask` | The same as `skip`. The run also ends with `exchange.requiresAction` and a `pendingApproval` of `kind: "terms"`, so you can ask your user, accept, and continue the thread. | +| `fail` | The run stops making calls as soon as it needs a gated provider. `exchange.error.code` is `THIRD_PARTY_DATA_TERMS_REQUIRED`, and `exchange.requiresAction` holds the calls to accept. This mode's behaviour may still change. | + +If you leave `onTermsRequired` out on a follow-up turn of a thread, the turn uses the previous turn's value. + +### Response fields + +These fields are on the `exchange` object of `GET /v2/agent/{id}`: + +- `skippedProviders` (any mode): one entry per gated provider that would have helped, with `provider`, `name`, `capability`, `adds` (what it would have added), `reason: "terms_required"`, `version` (the terms version) and `termsUrl` (where to accept in the dashboard). +- `requiresAction` (`ask` and `fail`): `type: "accept_terms"` and a `providers` list. Each provider carries the exact `show` (`terms/show`) and `accept` (`terms/accept`) calls to make, plus an `id`. In `ask` mode, `requiresAction.approvalId` is the id to answer when you continue the thread. `accept.options.digest` is `null` when the catalog didn't publish a digest, and `terms/show` returns it. +- `error` (`fail` only): `{ "code": "THIRD_PARTY_DATA_TERMS_REQUIRED", "message": "..." }`. + +```json +{ + "status": "completed", + "exchange": { + "enabled": true, + "onTermsRequired": "ask", + "paidCalls": 0, + "creditsUsed": null, + "skippedProviders": [ + { + "provider": "apollo", + "name": "Apollo", + "capability": "people/search", + "adds": "verified work emails and direct phone numbers", + "reason": "terms_required", + "version": "F-1.0.0", + "termsUrl": "https://www.firecrawl.dev/app/alexandria/apollo" + } + ], + "requiresAction": { + "type": "accept_terms", + "approvalId": "0199aaaa-0000-7000-8000-000000000000", + "providers": [ + { + "id": "apollo", + "provider": "apollo", + "name": "Apollo", + "capability": "people/search", + "version": "F-1.0.0", + "url": "https://www.firecrawl.dev/app/alexandria/apollo", + "show": { "provider": "firecrawl", "capability": "terms/show", "options": { "provider": "apollo" } }, + "accept": { + "provider": "firecrawl", + "capability": "terms/accept", + "options": { "provider": "apollo", "version": "F-1.0.0", "digest": "", "confirmed": true } + } + } + ] + } + }, + "pendingApproval": { + "id": "0199aaaa-0000-7000-8000-000000000000", + "kind": "terms", + "reason": "Apollo could add verified work emails and direct phone numbers.", + "calls": [], + "terms": [{ "id": "apollo", "provider": "apollo", "name": "Apollo", "version": "F-1.0.0", "url": "https://www.firecrawl.dev/app/alexandria/apollo" }], + "resolution": null + } +} +``` + +### Accept, then continue + +In `ask` mode: + +1. Show your user the terms. Run the provider's `show` call through [`/v2/scrape`](/features/alexandria) with `alexandria`. +2. Only if the user explicitly agrees, run its `accept` call the same way: + +```bash +curl -X POST https://api.firecrawl.dev/v2/scrape \ + -H "Authorization: Bearer $FIRECRAWL_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "alexandria": { + "provider": "firecrawl", + "capability": "terms/accept", + "options": { "provider": "apollo", "version": "F-1.0.0", "digest": "", "confirmed": true } + } + }' +``` + +3. Continue the same thread with `exchange.approve`. `callIds` lists the accepted provider `id`s, and leaving it out means all of them. The next turn uses those providers to fill the gap the previous answer named, rather than re-running everything. + +```bash +curl -X POST https://api.firecrawl.dev/v2/agent \ + -H "Authorization: Bearer $FIRECRAWL_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "threadId": "", + "prompt": "Continue with Apollo.", + "exchange": { + "approve": { "approvalId": "0199aaaa-0000-7000-8000-000000000000", "callIds": ["apollo"] } + } + }' +``` + +To go on without the provider, continue with `exchange.decline: { "approvalId": "..." }` instead. Declined providers aren't offered again for the rest of the thread. + +In `skip` and `fail` modes there's no pending approval to answer. After the terms are accepted, start a new run (or a new turn of the thread) and the provider is available. + ## Parameters | Parameter | Type | Required | Description | @@ -260,6 +372,7 @@ The `model` parameter is optional — every request runs `spark-2`: | `strictConstrainToURLs` | boolean | No | If `true`, the agent only visits the URLs provided in the `urls` array | | `webhook` | object | No | Webhook to receive agent lifecycle events (`agent.started`, `agent.action`, `agent.completed`, `agent.failed`, `agent.cancelled`). See the [webhook payloads](/api-reference/endpoint/webhook-agent-started) | | `maxCredits` | number | No | Maximum number of credits to spend on this agent task. Defaults to **2,500** if not set. The dashboard supports values up to **2,500**; for higher limits, set `maxCredits` via the API (values above 2,500 are always treated as paid requests). If the limit is reached, the job fails and **no data is returned**. Failed runs are not billed: credits used for AI reasoning are never charged on failure, any credits used for tool calls during the run (scraping, search, mapping, etc.) are refunded, and the response reports `creditsUsed: 0`. | +| `exchange.onTermsRequired` | string | No | What to do when an Alexandria data provider the agent would use needs terms your team has not accepted: `skip` (default), `ask`, or `fail`. See [Data providers that need terms](#data-providers-that-need-terms) | ## Agent vs Extract: What's Improved diff --git a/features/alexandria.mdx b/features/alexandria.mdx index 5eb85aa86..e7e5ed040 100644 --- a/features/alexandria.mdx +++ b/features/alexandria.mdx @@ -176,7 +176,7 @@ if catalogue.items and catalogue.items[0].get("next"): - **Tool discovery:** free. Use `sources: ["alexandria"]` for tools only, or `["web", "alexandria"]` to include paid web results. - **Execution:** charged at the tool's listed price. -- **Provider terms:** a `THIRD_PARTY_DATA_TERMS_REQUIRED` error includes `requiresAction.url`. An org admin must review and accept the terms there. Agents need explicit user authorization before accepting terms. +- **Provider terms:** a `THIRD_PARTY_DATA_TERMS_REQUIRED` error includes `requiresAction.url`. An org admin must review and accept the terms there. Agents need explicit user authorization before accepting terms. [Agent](/features/agent#data-providers-that-need-terms) runs skip providers whose terms your team hasn't accepted and report them in `exchange.skippedProviders`. From 1dc8bfbb3b806de215fd926a21e5941fc778f35a Mon Sep 17 00:00:00 2001 From: Rakshith Ramprakash Date: Fri, 25 Sep 2026 18:51:27 +1000 Subject: [PATCH 2/5] docs(agent): document exchange.decline.callIds for terms offers Co-Authored-By: Claude Opus 5.5 --- features/agent.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/agent.mdx b/features/agent.mdx index afb9f6321..a520a5733 100644 --- a/features/agent.mdx +++ b/features/agent.mdx @@ -356,7 +356,7 @@ curl -X POST https://api.firecrawl.dev/v2/agent \ }' ``` -To go on without the provider, continue with `exchange.decline: { "approvalId": "..." }` instead. Declined providers aren't offered again for the rest of the thread. +To go on without the provider, continue with `exchange.decline: { "approvalId": "...", "callIds": ["apollo"] }` instead. `callIds` lists the provider `id`s the user declined, and leaving it out declines the whole offer. Declined providers aren't offered again for the rest of the thread. Any providers on the offer that weren't declined can still be offered later. In `skip` and `fail` modes there's no pending approval to answer. After the terms are accepted, start a new run (or a new turn of the thread) and the provider is available. From d8b052c2f8cdb1f8fcaa0c87a54013041d982ea7 Mon Sep 17 00:00:00 2001 From: Rakshith Ramprakash Date: Sat, 26 Sep 2026 01:23:44 +1000 Subject: [PATCH 3/5] docs(agent): match extract-v3#182 at 67f40bb5 Fail mode also ends on an answerable terms pendingApproval; requiresAction always carries approvalId and is absent when the offer was deferred behind a paid-call approval; a null accept digest comes from terms/show; only the listed callIds count ([] names nobody, omitted means all). Co-Authored-By: Claude Opus 5.5 --- features/agent.mdx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/features/agent.mdx b/features/agent.mdx index a520a5733..57a6e015f 100644 --- a/features/agent.mdx +++ b/features/agent.mdx @@ -258,7 +258,7 @@ There is no auto-accept mode. Accepting a provider's terms always needs a person |---|---| | `skip` (default) | The run answers with accepted providers only and never blocks. `exchange.skippedProviders` lists the gated providers that would have helped. | | `ask` | The same as `skip`. The run also ends with `exchange.requiresAction` and a `pendingApproval` of `kind: "terms"`, so you can ask your user, accept, and continue the thread. | -| `fail` | The run stops making calls as soon as it needs a gated provider. `exchange.error.code` is `THIRD_PARTY_DATA_TERMS_REQUIRED`, and `exchange.requiresAction` holds the calls to accept. This mode's behaviour may still change. | +| `fail` | The run stops making calls as soon as it needs a gated provider, and `exchange.error.code` is `THIRD_PARTY_DATA_TERMS_REQUIRED`. Like `ask`, it also ends with `exchange.requiresAction` and a `pendingApproval` of `kind: "terms"`, so you can accept and continue the thread. This mode's behaviour may still change. | If you leave `onTermsRequired` out on a follow-up turn of a thread, the turn uses the previous turn's value. @@ -267,7 +267,8 @@ If you leave `onTermsRequired` out on a follow-up turn of a thread, the turn use These fields are on the `exchange` object of `GET /v2/agent/{id}`: - `skippedProviders` (any mode): one entry per gated provider that would have helped, with `provider`, `name`, `capability`, `adds` (what it would have added), `reason: "terms_required"`, `version` (the terms version) and `termsUrl` (where to accept in the dashboard). -- `requiresAction` (`ask` and `fail`): `type: "accept_terms"` and a `providers` list. Each provider carries the exact `show` (`terms/show`) and `accept` (`terms/accept`) calls to make, plus an `id`. In `ask` mode, `requiresAction.approvalId` is the id to answer when you continue the thread. `accept.options.digest` is `null` when the catalog didn't publish a digest, and `terms/show` returns it. +- `requiresAction` (`ask` and `fail`): `type: "accept_terms"`, an `approvalId` and a `providers` list. `approvalId` is always present. It's the id of the `terms` pending approval you answer when you continue the thread. Each provider carries the exact `show` (`terms/show`) and `accept` (`terms/accept`) calls to make, plus an `id`. `accept.options.digest` can be `null`. When it is, run `terms/show` first and send the digest it returns. +- If the same turn also stopped on a paid-call approval (`requireApproval`), that approval comes first. The turn then has `skippedProviders` but no `requiresAction`, and its `pendingApproval` is the paid-call one. The terms offer, with its own `approvalId`, arrives on the next turn. - `error` (`fail` only): `{ "code": "THIRD_PARTY_DATA_TERMS_REQUIRED", "message": "..." }`. ```json @@ -323,10 +324,10 @@ These fields are on the `exchange` object of `GET /v2/agent/{id}`: ### Accept, then continue -In `ask` mode: +In `ask` and `fail` modes: 1. Show your user the terms. Run the provider's `show` call through [`/v2/scrape`](/features/alexandria) with `alexandria`. -2. Only if the user explicitly agrees, run its `accept` call the same way: +2. Only if the user explicitly agrees, run its `accept` call the same way. If `accept.options.digest` is `null`, use the digest `terms/show` returned: ```bash curl -X POST https://api.firecrawl.dev/v2/scrape \ @@ -341,7 +342,7 @@ curl -X POST https://api.firecrawl.dev/v2/scrape \ }' ``` -3. Continue the same thread with `exchange.approve`. `callIds` lists the accepted provider `id`s, and leaving it out means all of them. The next turn uses those providers to fill the gap the previous answer named, rather than re-running everything. +3. Continue the same thread with `exchange.approve`. `callIds` lists the accepted provider `id`s. Only the ids you list count, so an empty list names nobody. Leaving `callIds` out means every provider on the offer. The next turn uses those providers to fill the gap the previous answer named, rather than re-running everything. ```bash curl -X POST https://api.firecrawl.dev/v2/agent \ @@ -356,9 +357,9 @@ curl -X POST https://api.firecrawl.dev/v2/agent \ }' ``` -To go on without the provider, continue with `exchange.decline: { "approvalId": "...", "callIds": ["apollo"] }` instead. `callIds` lists the provider `id`s the user declined, and leaving it out declines the whole offer. Declined providers aren't offered again for the rest of the thread. Any providers on the offer that weren't declined can still be offered later. +To go on without the provider, continue with `exchange.decline: { "approvalId": "...", "callIds": ["apollo"] }` instead. `callIds` lists the provider `id`s the user declined. An empty list declines nobody, and leaving it out declines the whole offer. Declined providers aren't offered again for the rest of the thread. Any providers on the offer that weren't declined can still be offered later. -In `skip` and `fail` modes there's no pending approval to answer. After the terms are accepted, start a new run (or a new turn of the thread) and the provider is available. +In `skip` mode there's no pending approval to answer. After the terms are accepted, start a new run (or a new turn of the thread) and the provider is available. ## Parameters From 5456e5d33361b83391e4915845e6b55e1bccd992 Mon Sep 17 00:00:00 2001 From: Rakshith Ramprakash Date: Sat, 26 Sep 2026 01:45:25 +1000 Subject: [PATCH 4/5] docs(agent): drop fail mode, terms deferral and decline.callIds Matches the extract-v3#182 scope cut: onTermsRequired is skip or ask, a terms offer is accepted or declined as a whole, and digest is always present and nullable. Co-Authored-By: Claude Opus 5.5 --- features/agent.mdx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/features/agent.mdx b/features/agent.mdx index 57a6e015f..25ee7e38d 100644 --- a/features/agent.mdx +++ b/features/agent.mdx @@ -258,7 +258,6 @@ There is no auto-accept mode. Accepting a provider's terms always needs a person |---|---| | `skip` (default) | The run answers with accepted providers only and never blocks. `exchange.skippedProviders` lists the gated providers that would have helped. | | `ask` | The same as `skip`. The run also ends with `exchange.requiresAction` and a `pendingApproval` of `kind: "terms"`, so you can ask your user, accept, and continue the thread. | -| `fail` | The run stops making calls as soon as it needs a gated provider, and `exchange.error.code` is `THIRD_PARTY_DATA_TERMS_REQUIRED`. Like `ask`, it also ends with `exchange.requiresAction` and a `pendingApproval` of `kind: "terms"`, so you can accept and continue the thread. This mode's behaviour may still change. | If you leave `onTermsRequired` out on a follow-up turn of a thread, the turn uses the previous turn's value. @@ -267,9 +266,7 @@ If you leave `onTermsRequired` out on a follow-up turn of a thread, the turn use These fields are on the `exchange` object of `GET /v2/agent/{id}`: - `skippedProviders` (any mode): one entry per gated provider that would have helped, with `provider`, `name`, `capability`, `adds` (what it would have added), `reason: "terms_required"`, `version` (the terms version) and `termsUrl` (where to accept in the dashboard). -- `requiresAction` (`ask` and `fail`): `type: "accept_terms"`, an `approvalId` and a `providers` list. `approvalId` is always present. It's the id of the `terms` pending approval you answer when you continue the thread. Each provider carries the exact `show` (`terms/show`) and `accept` (`terms/accept`) calls to make, plus an `id`. `accept.options.digest` can be `null`. When it is, run `terms/show` first and send the digest it returns. -- If the same turn also stopped on a paid-call approval (`requireApproval`), that approval comes first. The turn then has `skippedProviders` but no `requiresAction`, and its `pendingApproval` is the paid-call one. The terms offer, with its own `approvalId`, arrives on the next turn. -- `error` (`fail` only): `{ "code": "THIRD_PARTY_DATA_TERMS_REQUIRED", "message": "..." }`. +- `requiresAction` (`ask` only): `type: "accept_terms"`, an `approvalId` and a `providers` list. `approvalId` is always present. It's the id of the `terms` pending approval you answer when you continue the thread. Each provider carries the exact `show` (`terms/show`) and `accept` (`terms/accept`) calls to make, plus an `id`. Each provider's `digest` (and `accept.options.digest`) is always present and can be `null`, meaning the catalog didn't publish one. In that case, run `terms/show` first and send the digest it returns. ```json { @@ -300,6 +297,7 @@ These fields are on the `exchange` object of `GET /v2/agent/{id}`: "name": "Apollo", "capability": "people/search", "version": "F-1.0.0", + "digest": "", "url": "https://www.firecrawl.dev/app/alexandria/apollo", "show": { "provider": "firecrawl", "capability": "terms/show", "options": { "provider": "apollo" } }, "accept": { @@ -316,7 +314,7 @@ These fields are on the `exchange` object of `GET /v2/agent/{id}`: "kind": "terms", "reason": "Apollo could add verified work emails and direct phone numbers.", "calls": [], - "terms": [{ "id": "apollo", "provider": "apollo", "name": "Apollo", "version": "F-1.0.0", "url": "https://www.firecrawl.dev/app/alexandria/apollo" }], + "terms": [{ "id": "apollo", "provider": "apollo", "name": "Apollo", "version": "F-1.0.0", "digest": "", "url": "https://www.firecrawl.dev/app/alexandria/apollo" }], "resolution": null } } @@ -324,7 +322,7 @@ These fields are on the `exchange` object of `GET /v2/agent/{id}`: ### Accept, then continue -In `ask` and `fail` modes: +In `ask` mode: 1. Show your user the terms. Run the provider's `show` call through [`/v2/scrape`](/features/alexandria) with `alexandria`. 2. Only if the user explicitly agrees, run its `accept` call the same way. If `accept.options.digest` is `null`, use the digest `terms/show` returned: @@ -342,7 +340,7 @@ curl -X POST https://api.firecrawl.dev/v2/scrape \ }' ``` -3. Continue the same thread with `exchange.approve`. `callIds` lists the accepted provider `id`s. Only the ids you list count, so an empty list names nobody. Leaving `callIds` out means every provider on the offer. The next turn uses those providers to fill the gap the previous answer named, rather than re-running everything. +3. Continue the same thread with `exchange.approve`. The offer is accepted as a whole. The next turn uses those providers to fill the gap the previous answer named, rather than re-running everything. ```bash curl -X POST https://api.firecrawl.dev/v2/agent \ @@ -352,12 +350,12 @@ curl -X POST https://api.firecrawl.dev/v2/agent \ "threadId": "", "prompt": "Continue with Apollo.", "exchange": { - "approve": { "approvalId": "0199aaaa-0000-7000-8000-000000000000", "callIds": ["apollo"] } + "approve": { "approvalId": "0199aaaa-0000-7000-8000-000000000000" } } }' ``` -To go on without the provider, continue with `exchange.decline: { "approvalId": "...", "callIds": ["apollo"] }` instead. `callIds` lists the provider `id`s the user declined. An empty list declines nobody, and leaving it out declines the whole offer. Declined providers aren't offered again for the rest of the thread. Any providers on the offer that weren't declined can still be offered later. +To go on without the provider, continue with `exchange.decline: { "approvalId": "..." }` instead. This declines the whole offer, and its providers aren't offered again for the rest of the thread. In `skip` mode there's no pending approval to answer. After the terms are accepted, start a new run (or a new turn of the thread) and the provider is available. @@ -373,7 +371,7 @@ In `skip` mode there's no pending approval to answer. After the terms are accept | `strictConstrainToURLs` | boolean | No | If `true`, the agent only visits the URLs provided in the `urls` array | | `webhook` | object | No | Webhook to receive agent lifecycle events (`agent.started`, `agent.action`, `agent.completed`, `agent.failed`, `agent.cancelled`). See the [webhook payloads](/api-reference/endpoint/webhook-agent-started) | | `maxCredits` | number | No | Maximum number of credits to spend on this agent task. Defaults to **2,500** if not set. The dashboard supports values up to **2,500**; for higher limits, set `maxCredits` via the API (values above 2,500 are always treated as paid requests). If the limit is reached, the job fails and **no data is returned**. Failed runs are not billed: credits used for AI reasoning are never charged on failure, any credits used for tool calls during the run (scraping, search, mapping, etc.) are refunded, and the response reports `creditsUsed: 0`. | -| `exchange.onTermsRequired` | string | No | What to do when an Alexandria data provider the agent would use needs terms your team has not accepted: `skip` (default), `ask`, or `fail`. See [Data providers that need terms](#data-providers-that-need-terms) | +| `exchange.onTermsRequired` | string | No | What to do when an Alexandria data provider the agent would use needs terms your team has not accepted: `skip` (default) or `ask`. See [Data providers that need terms](#data-providers-that-need-terms) | ## Agent vs Extract: What's Improved From ff0af9d21f978ec8925532e1aedbdbe3a24e50da Mon Sep 17 00:00:00 2001 From: Rakshith Ramprakash Date: Sat, 26 Sep 2026 01:51:37 +1000 Subject: [PATCH 5/5] docs(agent): match the final extract-v3#182 contract No per-provider id; approve callIds/always are ignored on terms offers; no terms offer on a turn that ends on a paid-call approval; terms gating is rolling out. Co-Authored-By: Claude Opus 5.5 --- features/agent.mdx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/features/agent.mdx b/features/agent.mdx index 25ee7e38d..83ded290f 100644 --- a/features/agent.mdx +++ b/features/agent.mdx @@ -250,6 +250,8 @@ The `model` parameter is optional — every request runs `spark-2`: ## Data providers that need terms +Terms gating for Agent is rolling out. Until it's enabled for your runs, `onTermsRequired` has no effect, and none of the fields below appear. + Agent can call [Alexandria](/features/alexandria) data providers during a run, but only the ones whose data terms your team has accepted. A provider whose terms you haven't accepted is never called, whatever `exchange.onTermsRequired` is set to. The run carries on with the providers it can use, and the status response reports what it skipped. There is no auto-accept mode. Accepting a provider's terms always needs a person to agree to them, either in the [dashboard](https://www.firecrawl.dev/app/settings?tab=data-sources) or through a `terms/accept` call your application makes after its user has explicitly agreed. If you build an agent on top of Firecrawl, it must ask its user before calling `terms/accept`. A request for data isn't consent to a provider's terms. @@ -259,14 +261,14 @@ There is no auto-accept mode. Accepting a provider's terms always needs a person | `skip` (default) | The run answers with accepted providers only and never blocks. `exchange.skippedProviders` lists the gated providers that would have helped. | | `ask` | The same as `skip`. The run also ends with `exchange.requiresAction` and a `pendingApproval` of `kind: "terms"`, so you can ask your user, accept, and continue the thread. | -If you leave `onTermsRequired` out on a follow-up turn of a thread, the turn uses the previous turn's value. +If you leave `onTermsRequired` out on a follow-up turn of a thread, the turn uses the previous turn's value. If a turn ends on a paid-call approval (`requireApproval`), it has no terms offer. ### Response fields These fields are on the `exchange` object of `GET /v2/agent/{id}`: - `skippedProviders` (any mode): one entry per gated provider that would have helped, with `provider`, `name`, `capability`, `adds` (what it would have added), `reason: "terms_required"`, `version` (the terms version) and `termsUrl` (where to accept in the dashboard). -- `requiresAction` (`ask` only): `type: "accept_terms"`, an `approvalId` and a `providers` list. `approvalId` is always present. It's the id of the `terms` pending approval you answer when you continue the thread. Each provider carries the exact `show` (`terms/show`) and `accept` (`terms/accept`) calls to make, plus an `id`. Each provider's `digest` (and `accept.options.digest`) is always present and can be `null`, meaning the catalog didn't publish one. In that case, run `terms/show` first and send the digest it returns. +- `requiresAction` (`ask` only): `type: "accept_terms"`, an `approvalId` and a `providers` list. `approvalId` is always present. It's the id of the `terms` pending approval you answer when you continue the thread. Each provider carries the exact `show` (`terms/show`) and `accept` (`terms/accept`) calls to make. Each provider's `digest` (and `accept.options.digest`) is always present and can be `null`, meaning the catalog didn't publish one. In that case, run `terms/show` first and send the digest it returns. ```json { @@ -292,7 +294,6 @@ These fields are on the `exchange` object of `GET /v2/agent/{id}`: "approvalId": "0199aaaa-0000-7000-8000-000000000000", "providers": [ { - "id": "apollo", "provider": "apollo", "name": "Apollo", "capability": "people/search", @@ -314,7 +315,7 @@ These fields are on the `exchange` object of `GET /v2/agent/{id}`: "kind": "terms", "reason": "Apollo could add verified work emails and direct phone numbers.", "calls": [], - "terms": [{ "id": "apollo", "provider": "apollo", "name": "Apollo", "version": "F-1.0.0", "digest": "", "url": "https://www.firecrawl.dev/app/alexandria/apollo" }], + "terms": [{ "provider": "apollo", "name": "Apollo", "version": "F-1.0.0", "digest": "", "url": "https://www.firecrawl.dev/app/alexandria/apollo" }], "resolution": null } } @@ -340,7 +341,7 @@ curl -X POST https://api.firecrawl.dev/v2/scrape \ }' ``` -3. Continue the same thread with `exchange.approve`. The offer is accepted as a whole. The next turn uses those providers to fill the gap the previous answer named, rather than re-running everything. +3. Continue the same thread with `exchange.approve`. The offer is accepted as a whole, and `callIds` and `always` are ignored on it. The next turn uses those providers to fill the gap the previous answer named, rather than re-running everything. ```bash curl -X POST https://api.firecrawl.dev/v2/agent \