This guide walks you through the steps to create and execute a trade from the
taker side on StableFX.
Prerequisites
Before you begin this quickstart, ensure you have:
- Obtained an API key for StableFX from Circle
- Set up a web3 provider or wallet that supports EIP-712 signatures and Permit2
- Granted a token allowance to the
Permit2 contract. See
How-to: Grant USDC Allowance to Permit2
for more information.
- Installed cURL on your development machine
This quickstart provides API request examples in cURL format, along with example
responses.
Part 1: Request a quote
Request a quote for a USDC to EURC trade using the
create a quote endpoint. You should
provide a value for the amount parameter in either the to or from fields,
but not both.
curl --request POST \
--url https://api-sandbox.circle.com/v1/exchange/stablefx/quotes \
--header 'Accept: application/json' \
--header 'Authorization: Bearer ${YOUR_API_KEY}' \
--header 'Content-Type: application/json' \
--data '
{
"from": {
"currency": "USDC",
"amount": "1000.00"
},
"to": {
"currency": "EURC"
},
"tenor": "instant"
}
'
Response
{
"id": "c4d1da72-111e-4d52-bdbf-2e74a2d803d5",
"rate": "0.9150",
"from": {
"currency": "USDC",
"amount": "1000.00"
},
"to": {
"currency": "EURC",
"amount": "915.00"
},
"timestamp": "2025-08-07T11:00:00Z",
"expiry": "2025-08-07T11:05:00Z",
"fee": {
"currency": "USDC",
"amount": "1.50"
}
}
Part 2: Create a trade
Accept the quote and create a trade on StableFX using the
create a trade endpoint. You need to
provide the quote ID and a randomly generated idempotency key in UUIDv4 format.
curl --request POST \
--url https://api-sandbox.circle.com/v1/exchange/stablefx/trades \
--header 'Accept: application/json' \
--header 'Authorization: Bearer ${YOUR_API_KEY}' \
--header 'Content-Type: application/json' \
--data '
{
"idempotencyKey": "${randomUUID}",
"quoteId": "${stablefx_quote_id}"
}
'
Response
{
"id": "uuid-trade",
"from": {
"currency": "USDC",
"amount": "1000.00"
},
"to": {
"currency": "EURC",
"amount": "915.00"
},
"status": "created",
"createDate": "2025-08-07T11:01:00Z",
"updateDate": "2025-08-07T11:01:00Z",
"quoteId": "uuid-quote",
"rate": "0.9150"
}
Part 3: Confirm trade intent
Confirm your trade intent through the StableFX API before submitting the trade
on the blockchain.
3.1. Generate taker signature data
To submit the trade, you must first sign the trade intent typed data with an
EIP-712 signature. To get the data to sign, call the
generate trade presign data
endpoint. Your request must include the ID of the trade in a confirmed status
and the address of the recipient of the trade.
curl --request GET \
--url https://api-sandbox.circle.com/v1/exchange/stablefx/signatures/presign/taker/${stablefx_trade_id}?recipientAddress=${recipient_address} \
--header 'Accept: application/json' \
--header 'Authorization: Bearer ${YOUR_API_KEY}'
'
Response
{
"data": {
"typedData": {
"domain": {
"name": "FxEscrow",
"version": "1",
"chainId": 5042002,
"verifyingContract": "0x1f91886c7028986ad885ffcee0e40b75c9cd5ac1"
},
"types": {
"EIP712Domain": [
{
"name": "name",
"type": "string"
},
{
"name": "version",
"type": "string"
},
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
],
"Consideration": [
{
"name": "quoteId",
"type": "bytes32"
},
{
"name": "base",
"type": "address"
},
{
"name": "quote",
"type": "address"
},
{
"name": "baseAmount",
"type": "uint256"
},
{
"name": "quoteAmount",
"type": "uint256"
},
{
"name": "maturity",
"type": "uint256"
}
],
"TakerDetails": [
{
"name": "consideration",
"type": "Consideration"
},
{
"name": "recipient",
"type": "address"
},
{
"name": "fee",
"type": "uint256"
},
{
"name": "nonce",
"type": "uint256"
},
{
"name": "deadline",
"type": "uint256"
}
]
},
"primaryType": "TakerDetails",
"message": {
"consideration": {
"quoteId": "0x00000000000000000000000000000000f054a0d91c1747f3be7885d5c38dd0f1",
"base": "0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a",
"quote": "0x3600000000000000000000000000000000000000",
"quoteAmount": 1000000000,
"baseAmount": 908250000,
"maturity": 1762189673
},
"fee": 181650,
"nonce": 25426119,
"deadline": 1762188474,
"recipient": "0x379c868f6064d9c0564df05dcca170d64f8aa5e3"
}
}
}
}
3.2. Sign the data
Using a Permit2-compliant EIP-712 compatible wallet or application, sign the
data returned in the previous step.
3.3. Submit the trade signature
Confirm your trade intent by submitting the signed data to the
register trade signature
endpoint. This request must include the trade ID, the side of the trade that you
are taking, the address of the wallet that signed the typed data, the message of
the typed data, and the taker’s signature.
curl --request POST \
--url https://api-sandbox.circle.com/v1/exchange/stablefx/signatures \
--header 'Accept: application/json' \
--header 'Authorization: Bearer ${YOUR_API_KEY}' \
--header 'Content-Type: application/json' \
--data '
{
"tradeId": "${stablefx_trade_id}",
"type": "taker",
"address": "0xabc123...",
"details": {
"consideration": {
"quoteId": 1234,
"base": "0x...",
"quote": "0x...",
"baseAmount": "100000000",
"quoteAmount": "90000000",
"maturity": 1752148800
},
"recipient": "0xabc123...",
"nonce": "123456",
"deadline": 1752149700,
"fee": "100000"
},
"signature": "0xsignature"
}
'
If the signed data is accepted, the API returns a blank 200 response.
3.4. Confirm the trade is ready for funding
Before you submit funds onchain, you should confirm that the trade is ready for
funding. To do this, call the
get a trade by ID endpoint. A
trade is ready for funding when the status is pending_settlement.
curl --request GET \
--url https://api-sandbox.circle.com/v1/exchange/stablefx/trades/${stablefx_trade_id}?type=taker&status=pending_settlement \
--header 'Accept: application/json' \
--header 'Authorization: Bearer ${YOUR_API_KEY}'
Response
{
"id": "c4d1da72-111e-4d52-bdbf-2e74a2d803d5",
"contractTradeId": "24",
"status": "pending_settlement",
"rate": 0.915,
"from": {
"currency": "USDC",
"amount": "1000"
},
"to": {
"currency": "EURC",
"amount": "915"
},
"createDate": "2023-01-01T12:04:05Z",
"updateDate": "2023-01-01T12:04:05Z",
"quoteId": "c4d1da72-111e-4d52-bdbf-2e74a2d803d5"
}
Part 4: Submit the funds onchain
Next, you need to deliver the funds onchain to complete the trade. The StableFX
API can handle the onchain transaction for you using the Permit2 contract.
4.1. Get the funding signature data
To use the StableFX API to deliver the funds onchain, you must first sign the
funding typed data with an EIP-712 signature. To get the data to sign, call the
generate funding presign data
endpoint. Your request must include the contract ID of the trade and the side of
the trade that you are taking. The following an example request:
curl --request POST \
--url https://api-sandbox.circle.com/v1/exchange/stablefx/signatures/funding/presign \
--header 'Accept: application/json' \
--header 'Authorization: Bearer ${YOUR_API_KEY}' \
--header 'Content-Type: application/json' \
--data '
{
"contractTradeIds": ["${stablefx_contract_trade_id}"],
"type": "taker"
}
'
Response
{
"typedData": {
"domain": {
"name": "Permit2",
"chainId": 11155111,
"verifyingContract": "0xffd21ca8F0876DaFAD7de09404E0c1f868bbf1AE"
},
"types": {
"EIP712Domain": [
{
"name": "name",
"type": "string"
},
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
],
"TokenPermissions": [
{
"name": "token",
"type": "address"
},
{
"name": "amount",
"type": "uint256"
}
],
"SingleTradeWitness": [
{
"name": "id",
"type": "uint256"
}
],
"PermitWitnessTransferFrom": [
{
"name": "permitted",
"type": "TokenPermissions"
},
{
"name": "spender",
"type": "address"
},
{
"name": "nonce",
"type": "uint256"
},
{
"name": "deadline",
"type": "uint256"
},
{
"name": "witness",
"type": "SingleTradeWitness"
}
]
},
"primaryType": "PermitWitnessTransferFrom",
"message": {
"permitted": {
"token": "0xTOKEN",
"amount": "1000"
},
"spender": "0xffd21ca8F0876DaFAD7de09404E0c1f868bbf1AE",
"nonce": "42",
"deadline": "1735689600",
"witness": {
"id": "10"
}
}
}
}
4.2. Sign the funding data
Using a Permit2-compliant EIP-712 compatible wallet or application, sign the
data from the typedData field returned in the previous step.
4.3. Submit the funding signature
To deliver the funds onchain, you must submit the taker-specific Permit2 data
along with your signature to the
fund trade endpoint. The endpoint
allows you to submit either a single object or a batch of objects along with the
required signatures.
The following is an example request for a single trade:
curl --request POST \
--url https://api-sandbox.circle.com/v1/exchange/stablefx/fund \
--header 'Accept: application/json' \
--header 'Authorization: Bearer ${YOUR_API_KEY}' \
--header 'Content-Type: application/json' \
--data '
{
"type": "taker",
"signature": "0xsignature",
"permit2": {
"permitted": {
"token": "0xTOKEN1",
"amount": "1000"
},
"spender": "0xTOKEN1",
"nonce": "123456",
"deadline": 1752149700,
"witness": {
"id": "123456"
}
}
}
'
If the signed data is accepted, the API returns a blank 200 response.
Note: It’s not required to submit the funding transaction through the
StableFX API. You can submit the transaction onchain using your own web3
provider or wallet.
4.4. Confirm the trade is funded
To confirm that the trade is funded, call the
get a trade by ID endpoint. A
trade is funded when the status is taker_funded.
curl --request GET \
--url https://api-sandbox.circle.com/v1/exchange/stablefx/trades/${stablefx_trade_id}?type=taker&status=completed \
--header 'Accept: application/json' \
--header 'Authorization: Bearer ${YOUR_API_KEY}'
Response
{
"id": "c4d1da72-111e-4d52-bdbf-2e74a2d803d5",
"contractTradeId": "24",
"status": "taker_funded",
"rate": 0.915,
"from": {
"currency": "USDC",
"amount": "1000"
},
"to": {
"currency": "EURC",
"amount": "915"
},
"createDate": "2023-01-01T12:04:05Z",
"updateDate": "2023-01-01T12:04:05Z",
"quoteId": "c4d1da72-111e-4d52-bdbf-2e74a2d803d5"
}