Quickstart
Create an account, open a conversation, and get your first agent response. This run disables tools, so it needs no agent wallet or funded balance.
Before you begin
Obtain your deployment's API base URL, a partner API key, and a valid end-user access token from the configured identity provider. Fin does not issue human login tokens. Your deployment also needs model access and available execution budget.
These examples use Bash, curl, jq and uuidgen. Set the values in a trusted terminal or backend; keep the partner key out of browser code. Replace the placeholder URL with your API origin, without a trailing /v1.
FIN_BASE_URL=https://api.example.test
FIN_API_KEY=your_partner_key
FIN_USER_TOKEN=your_user_access_token
FIN_AUTH=(-H "X-Api-Key: $FIN_API_KEY" -H "Authorization: Bearer $FIN_USER_TOKEN")
See Authentication for how the two credentials work together.
1. Provision the user
users.ensure creates the verified identity's Fin account if needed, or returns the existing account. Save its userId for subsequent paths; it is not the identity provider's subject.
FIN_USER_ID=$(curl --fail-with-body -sS -X POST "$FIN_BASE_URL/v1/users" \
"${FIN_AUTH[@]}" -H 'Content-Type: application/json' -d '{}' | jq -er '.userId')
2. Open a conversation
conversations.create opens a conversation and returns 201 with its id. Save and reuse that id to continue the same thread; create another conversation when your application needs a separate one. Fin does not designate a special main conversation.
FIN_CONVERSATION_ID=$(curl --fail-with-body -sS \
-X POST "$FIN_BASE_URL/v1/users/$FIN_USER_ID/conversations" \
"${FIN_AUTH[@]}" -H 'Content-Type: application/json' -d '{}' | jq -er '.id')
3. Start a run
Generate one runId for this instruction and retain it. Reuse the same id if you need to retry this start after a timeout. Generate a new id for a new instruction.
FIN_RUN_ID=$(uuidgen | tr '[:upper:]' '[:lower:]')
curl --fail-with-body -sS \
-X POST "$FIN_BASE_URL/v1/users/$FIN_USER_ID/agent-runs" \
"${FIN_AUTH[@]}" -H 'Content-Type: application/json' \
-d "$(jq -n --arg runId "$FIN_RUN_ID" --arg conversationId "$FIN_CONVERSATION_ID" \
'{conversationId: $conversationId, text: "Say hello in one sentence.", runId: $runId, profile: {tools: []}}')"
A successful response is 202 with the accepted runId. It confirms admission, not completion. The explicit empty profile.tools list prevents tool calls. Omitting that list allows the deployment's registered tools, subject to policy and consent.
4. Follow the response
Open the conversation stream. It replays the journal, so opening it after the start response does not lose recorded events.
curl --fail-with-body -sSN \
"$FIN_BASE_URL/v1/users/$FIN_USER_ID/conversations/$FIN_CONVERSATION_ID/stream?cursor=0" \
"${FIN_AUTH[@]}" -H 'Accept: text/event-stream'
Show agent.text_delta as temporary text. Use durable agent.entry for the saved reply and run.ended for the recorded outcome. The stream stays open for future work; press Ctrl-C to close this terminal connection. See Streaming and events for reconnect handling.
You can also read a run's state without holding a connection open:
curl --fail-with-body -sS \
"$FIN_BASE_URL/v1/users/$FIN_USER_ID/agent-runs/$FIN_RUN_ID" \
"${FIN_AUTH[@]}"
status reports running, completed, failed, aborted or paused. Read response.text for the retained public assistant answer and response.completeness for a complete or partial ending. response is null until an answer is recorded. output separately carries a structured result when the run requested a profile.outputContract; output: null does not mean a text response failed.
When you enable tools
An action needing consent can open an approval and pause the run. approval.opened announces it, and the run's pendingApprovals lists undecided approvals. Fetch approvals.get for the persisted tool input, fingerprint, summary and authorization contract. Show the exact assets, amounts, destinations, limits and validity before asking the user to decide. A friendly summary alone is not the consent payload.
For an approval opened by a run, call approvals.approve or approvals.deny only after that user decision. Successful decisions resume work automatically in the same conversation; do not send a second start.
Keep following the conversation after approval.decided, or poll the same run id until its status is completed, failed or aborted. Approval resume does not emit another run.started; the public run keeps the same id and admitted input. A run.ended frame with status: "paused" is not terminal. If its pending approval expires, Fin records Stop and ends the run as aborted.
If resuming cannot be admitted, the decision is not recorded and the approval remains pending. If it expired or was already decided, handle 409 approval_not_pending by refreshing state. An approved or consumed approval does not establish settlement. Read financial operations for the exact request, consent, execution plan and receipts:
curl --fail-with-body -sS \
"$FIN_BASE_URL/v1/users/$FIN_USER_ID/financial-operations?runId=$FIN_RUN_ID" \
"${FIN_AUTH[@]}"
Direct withdrawals
Withdrawals use a separate review and confirmation flow. Review the terms returned by wallets.prepareWithdrawal, then pass its approvalId and unchanged fingerprint to wallets.confirmWithdrawal. Do not send a withdrawal approval to the generic approval endpoint. Retain its operationId to read the result; a direct withdrawal has no conversation or agent run.
Continue building
- Core concepts: resource ownership, lifecycle and consent.
- Streaming and events: durable history and reconnection.
- Financial operations: consent, settlement and uncertain results.
- Errors: machine-readable refusals and retry handling.
- Docs for agents: Markdown exports and the documentation MCP server.