Appearance
Deals API
Read any deal, make an offer on a registration, and answer the offers on yours. What each deal type means, and every rule an offer is held to, is in Ownership Deals.
Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
GET | /api/v2/deals/{dealId} | Optional session | Get one deal, with its events |
GET | /api/v2/deals/{dealId}/events | Optional session | Get a deal's event history |
GET | /api/v2/deals/mine | Session | Your deals, with their agreed terms |
GET | /api/v2/deals/symbol/{symbol} | API key | Deals on a ticker |
GET | /api/v2/deals/address/{address} | API key | Deals a wallet is a party to |
GET | /api/v2/registration/{id}/deals | API key | Deals on one registration |
GET | /api/v2/deals/group/{groupId} | API key | A set of offers that take effect together |
GET | /api/v2/deals/limits | API key | The numeric deal rules in force |
GET | /api/v2/deals/fee-preview | API key | Check an amount against the minimum offer |
GET | /api/v2/deals/prices | API key | USD prices for deal currencies |
POST | /api/v2/deals/verify-funds | Session | Prove you hold what you are about to offer |
POST | /api/v2/deals/offer | Session | Make an offer |
POST | /api/v2/deals/{dealId}/accept | Session | Accept an offer on your registration |
POST | /api/v2/deals/{dealId}/reject | Session | Reject an offer on your registration |
POST | /api/v2/deals/{dealId}/cancel | Session | Cancel a deal you are a party to |
POST | /api/v2/deals/{dealId}/reverify-funds | Session | Re-check a party's funds against an existing deal |
GET | /api/v2/deals/offer-floor | Session | Your offer settings |
PUT | /api/v2/deals/offer-floor | Session | Set a floor, or stop offers |
Settlement (payment received, burn consent, the Solana settlement signatures and transactions) runs from the deal page in the app. It is not covered here.
Authentication
Every request carries your X-API-Key. See Authentication.
Session routes also need a wallet session: Authorization: Bearer <JWT>. An API key is not accepted as the bearer on any deal route. Every write acts as the signed-in wallet, and a wallet named in the body must be one that session controls, or the request is refused with 403.
To get a session without a browser:
GET /api/v2/auth/noncereturns{ "nonce": "…" }- Sign a Sign-In with Ethereum message with domain
chaindaddy.ioand that nonce, from the wallet you will act as POST /api/v2/auth/login/siwewith{ "message", "signature" }returns{ "accessToken": "…" }
A Solana wallet uses POST /api/v2/auth/login/siws. Send the token as the bearer, alongside your API key.
Optional session routes answer anyone. With a session they also say what you may do, and a party sees what an outsider does not.
Get a Deal
GET /api/v2/deals/{dealId}One deal, its event history, its attestations and its recorded payments.
What a party sees
| Field | Outsider | Party (with a session) |
|---|---|---|
A collab's agreed terms in events | Removed | Included |
attestations[].signature, message_hash, domain_separator | Empty | Included |
actions | null | What you may do now, possibly [] |
Response Format
json
{
"success": true,
"deal": {
"id": "7b1e5c2a-3f0d-4a51-9c7e-2d8f6a1b4c90",
"deal_type": "buyout",
"status": "draft",
"stage": "awaiting_response",
"seller_address": "0x1234567890abcdef1234567890abcdef12345678",
"crown_holder": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
"symbol": "PEPE",
"chain_id": 8453,
"agreed_amount": "25000",
"agreed_currency": "USDC",
"message": "Happy to talk terms.",
"fund_verification_pct": 100,
"burn_consent_recorded": false,
"migration_window_end": "2026-11-15T18:00:00Z",
"deadline": "2026-11-15T18:00:00Z",
"deadline_kind": "migration_window",
"created_at": "2026-09-16T18:00:00Z",
"updated_at": "2026-09-16T18:00:00Z",
"actions": ["cancel"],
"events": [
{
"id": "c0a8f1d2-…",
"deal_id": "7b1e5c2a-3f0d-4a51-9c7e-2d8f6a1b4c90",
"event_type": "deal_created",
"event_data": {},
"actor_address": "0x1234567890abcdef1234567890abcdef12345678",
"created_at": "2026-09-16T18:00:00Z"
}
]
}
}Deal Fields
| Field | Type | Description |
|---|---|---|
id | string | Deal ID |
deal_type | string | buyout, project_sale, merge (a consolidation) or collab |
status | string | draft, pending, announced, paused, payment_pending, executing, migrating, completed, failed, cancelled or closed |
stage | string | Where the deal is, in one word. Read this rather than status: awaiting_response, announcement, swap_window, awaiting_consent, awaiting_dispatch, settling, awaiting_launch, completed, closed |
deadline | string | The one clock that matters at this stage, if one is running |
deadline_kind | string | What deadline counts down: offer, announcement, migration_window, burn_consent or settlement_window |
seller_address | string | The wallet that made the offer. The name reads backwards: it is the acquirer |
buyer_address | string | The registration's holder, written when they accept |
crown_holder | string | The wallet holding the registration now. Set on single-deal reads, and on list rows waiting on you |
symbol | string | Ticker |
chain_id | integer | Chain the registration is on. Solana is 900900 |
token_address | string | The token the deal names, when a ticker has more than one on a chain |
group_id | string | Set when the offer is one of a group |
agreed_amount | string | Offer amount, in agreed_currency |
agreed_currency | string | USDC, USDT, ETH, SOL, or a token contract address |
message | string | The offerer's message |
response_message | string | The holder's reply, or their reason for rejecting |
fund_verification_pct | integer | How much of the offer the offerer proved they held |
expires_at | string | When an unanswered offer lapses. Absent on a deal with its own window |
migration_window_end | string | When a buyout's redemption window or a consolidation's swap window closes |
burn_consent_recorded | boolean | Whether the holder has signed settlement consent. Never the consent itself |
burn_consent_expiry | integer | When that consent stops working, unix seconds |
burn_tx_hash, mint_tx_hash | string | Settlement transactions, once broadcast |
completed_at, failure_reason | string | Set when the deal ends |
actions | array | What this session may do now: accept, reject, cancel, sign_burn_consent, launch_collab. null on a read without a session |
events | array | Every event, oldest first |
attestations | array | Signed statements by the parties, such as payment received |
payments | array | Recorded payments, with their transaction and verification state |
Code Examples
bash
curl https://api.chaindaddy.io/api/v2/deals/7b1e5c2a-3f0d-4a51-9c7e-2d8f6a1b4c90 \
-H "X-API-Key: cd_live_abc123..."javascript
async function getDeal(apiKey, dealId, sessionToken) {
const headers = { 'X-API-Key': apiKey };
// Optional: a party with a session also gets actions, terms and signatures
if (sessionToken) headers.Authorization = `Bearer ${sessionToken}`;
const res = await fetch(`https://api.chaindaddy.io/api/v2/deals/${dealId}`, { headers });
if (res.status === 404) return null;
const { deal } = await res.json();
return deal;
}Get Deal Events
GET /api/v2/deals/{dealId}/eventsThe event history alone, oldest first. The same party rule applies to a collab's terms.
json
{
"dealId": "7b1e5c2a-3f0d-4a51-9c7e-2d8f6a1b4c90",
"events": [
{ "id": "c0a8f1d2-…", "deal_id": "7b1e5c2a-…", "event_type": "deal_created", "event_data": {}, "created_at": "2026-09-16T18:00:00Z" }
]
}List Your Deals
GET /api/v2/deals/mineEvery deal any wallet on your session is a party to, including offers waiting on you. The only list that carries the agreed terms.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
deal_type | string | No | buyout, project_sale, merge or collab. An unknown value is a 400 |
status | string | No | Any deal status. An unknown value is a 400 |
limit | integer | No | Page size (default: 20) |
offset | integer | No | Rows to skip (default: 0) |
json
{
"deals": [ { "id": "7b1e5c2a-…", "deal_type": "buyout", "terms": { "redemptionWindow": 60 }, "actions": ["cancel"] } ],
"total": 1,
"limit": 20,
"offset": 0,
"deal_type": "",
"status": ""
}List Deals
GET /api/v2/deals/symbol/{symbol}
GET /api/v2/deals/address/{address}
GET /api/v2/registration/{id}/dealsPublic lists. None carries agreed terms: use your own list for those. On the registration route, {id} is the registration's token ID.
| Parameter | Applies to | Description |
|---|---|---|
chainId | symbol | Limit to one chain |
role | address | seller (offers the wallet made) or buyer (deals it accepted). Omit for both, plus offers waiting on it |
limit | all | Page size (default: 20) |
offset | all | Rows to skip (default: 0) |
Each answers { "deals": [...], "total", "limit", "offset" }, plus what was asked: symbol, address and role, or tokenId, symbol and chainId.
bash
curl "https://api.chaindaddy.io/api/v2/deals/symbol/PEPE?chainId=8453" \
-H "X-API-Key: cd_live_abc123..."Get a Deal Group
GET /api/v2/deals/group/{groupId}Offers made as one set take effect only when every member is accepted. A deal's group_id names its set.
json
{
"group": { "id": "…" },
"deals": [ { "id": "…", "symbol": "PEPE", "chain_id": 8453, "status": "draft" } ],
"total": 3,
"accepted": 2,
"complete": false,
"pending": [ { "deal_id": "…", "symbol": "PEPE", "chain_id": 8453, "status": "draft" } ]
}pending names the members still waiting on their holder.
Get Deal Limits
GET /api/v2/deals/limitsThe numeric rules this environment enforces, in whole dollars.
json
{
"collabMinLiquidityUsd": 1000,
"minOfferUsd": 500
}Preview an Offer
GET /api/v2/deals/fee-previewChecks an amount against the minimum offer before you make it.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
dealValue | integer | Yes | Amount times 1,000,000, in currency. 1000000000 is 1,000 USDC |
currency | string | No | USDC (default), USDT, ETH, SOL, or a token contract address |
chainId | integer | No | Chain, for pricing a token contract address |
mcapEstimate | integer | No | Market cap in USD times 1,000,000. Sizes the minimum as a share of it |
Response Format
json
{
"facilitationFee": 50000000,
"minimumOffer": 500000000,
"mcapEstimate": 0,
"effectiveRate": 5,
"dealValueUsd": 1000000000,
"currency": "USDC"
}| Field | Type | Description |
|---|---|---|
minimumOffer | integer | The smallest offer allowed, USD times 1,000,000 |
dealValueUsd | integer | Your amount in USD, times 1,000,000 |
currency | string | The currency measured |
priceUsd | number | The price used to convert it. Absent for dollar stablecoins |
facilitationFee, effectiveRate | number | Legacy figures. No fee is charged on any deal |
An amount below the minimum is a 400 naming the minimum. So is a currency with no USD price: an offer that cannot be measured is refused, not guessed.
Get Deal Prices
GET /api/v2/deals/pricesUSD prices for the deal currencies, refreshed every five minutes.
json
{ "ETH": 3120.55, "SOL": 162.4, "USDC": 1, "USDT": 1 }Verify Funds
POST /api/v2/deals/verify-fundsReads your balance and, when it covers the offer, signs an attestation. A buyout, a project acquisition or a collab is refused without a passing check from the last hour, on the same currency and chain, from a wallet on your session.
A balance read, not a lock. Nothing is escrowed.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
walletAddress | string | Yes | The wallet to check. Must be on your session |
currency | string | Yes | Offer currency. On Solana, USDC only. For a collab, USDC |
offerAmount | number | Yes | The offer in currency, as a person would type it: 25000 is 25,000 USDC. For a collab, the liquidity it commits in dollars |
chainId | integer | Yes | Chain the offer is on |
symbol | string | No | Ticker |
Response Format
json
{
"verified": true,
"balance": "31000000000",
"requiredAmount": "25000000000",
"requiredPct": 100,
"attestation": {
"hash": "0x…",
"signature": "0x…",
"blockNumber": 19876543,
"timestamp": 1789581600
}
}| Field | Type | Description |
|---|---|---|
verified | boolean | Whether the balance covers the required amount |
balance | string | Balance read, in the token's base units |
requiredAmount | string | What the offer needs, in base units |
requiredPct | integer | Share of the offer your plan must hold: 100, or 80 for a paying Partner or Enterprise account |
shortfall | string | How much more you need, in base units. Only when not verified |
attestation | object | The signed attestation. Only when verified |
POST /api/v2/deals/{dealId}/reverify-funds takes the same body and re-checks against an existing deal. Only a party to that deal may file one.
Make an Offer
POST /api/v2/deals/offerMakes the offer and notifies the registration's holder. Checks run in this order, and the first to fail answers:
- The minimum offer (
400) - The holder's own settings: offers off, or below their floor (
403) - Verified funds (
402) - Your weekly offer count and the value you have standing (
429)
Every limit is in Rules and limits.
Making an offer, making a group and cancelling share a budget of 10 requests a minute per account.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
deal_type | string | Yes | buyout, project_sale, merge or collab. Required: a missing type is refused, never assumed |
symbol | string | Yes | Ticker |
chain_id | integer or string | Yes | Chain ID, or a chain key such as "base" |
token_address | string | No | Which token, when a ticker has more than one on the chain |
buyer_address | string | No | The wallet you offer from. Defaults to your session's wallet |
agreed_amount | string | Priced types | Offer amount, e.g. "25000". Required for buyout and project_sale |
agreed_currency | string | Priced types | USDC, USDT, ETH, a token contract address, or on Solana USDC |
payment_method | string | No | How you will pay. escrow is refused |
message | string | No | A note to the holder |
offer_window_days | integer | No | 7, 14, 30, 60 or 90 (default 30). Refused on a deal with its own window: a buyout's redemption window or a consolidation's swap window |
buyout_terms | object | No | redemptionWindow: the days holders have to redeem, 30 to 730 |
migration_terms | object | Merge | swapWindow: the days holders have to swap, 1 to 730. Required |
acquisition_terms | object | No | Terms of a project acquisition |
collab_terms | object | Collab | proposedSymbol (up to 16 characters), partnerShareBps (1 to 9999, the partner's share of the creator fee), liquidityCommitmentUsd (at least collabMinLiquidityUsd) |
Response Format
json
{
"success": true,
"dealId": "7b1e5c2a-3f0d-4a51-9c7e-2d8f6a1b4c90",
"status": "draft",
"deal": { "id": "7b1e5c2a-…", "deal_type": "buyout", "status": "draft" },
"message": "Offer created. The token owner will be notified."
}Code Examples
bash
# 1. Verify funds
curl -X POST https://api.chaindaddy.io/api/v2/deals/verify-funds \
-H "X-API-Key: cd_live_abc123..." \
-H "Authorization: Bearer eyJ..." \
-H "Content-Type: application/json" \
-d '{ "walletAddress": "0x1234...5678", "currency": "USDC", "offerAmount": 25000, "chainId": 8453 }'
# 2. Make the offer within the hour
curl -X POST https://api.chaindaddy.io/api/v2/deals/offer \
-H "X-API-Key: cd_live_abc123..." \
-H "Authorization: Bearer eyJ..." \
-H "Content-Type: application/json" \
-d '{
"deal_type": "buyout",
"symbol": "PEPE",
"chain_id": 8453,
"agreed_amount": "25000",
"agreed_currency": "USDC",
"buyout_terms": { "redemptionWindow": 60 },
"message": "Happy to talk terms."
}'javascript
const BASE_URL = 'https://api.chaindaddy.io';
async function makeBuyoutOffer({ apiKey, sessionToken, wallet, symbol, chainId, amount }) {
const headers = {
'X-API-Key': apiKey,
Authorization: `Bearer ${sessionToken}`,
'Content-Type': 'application/json',
};
// 1. Prove the funds. The check lasts one hour.
const check = await fetch(`${BASE_URL}/api/v2/deals/verify-funds`, {
method: 'POST',
headers,
body: JSON.stringify({ walletAddress: wallet, currency: 'USDC', offerAmount: amount, chainId }),
}).then(r => r.json());
if (!check.verified) throw new Error(`Short by ${check.shortfall} base units`);
// 2. Make the offer
const res = await fetch(`${BASE_URL}/api/v2/deals/offer`, {
method: 'POST',
headers,
body: JSON.stringify({
deal_type: 'buyout',
symbol,
chain_id: chainId,
agreed_amount: String(amount),
agreed_currency: 'USDC',
buyout_terms: { redemptionWindow: 60 },
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
return data.dealId;
}Accept or Reject an Offer
POST /api/v2/deals/{dealId}/accept
POST /api/v2/deals/{dealId}/rejectOnly the wallet holding the registration may answer. Accepting moves nothing on chain and costs no gas.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
wallet_address | string | No | The holding wallet. Defaults to your session's wallet |
response_message | string | No | Your reply, or your reason for rejecting |
Response Format
json
{
"success": true,
"dealId": "7b1e5c2a-3f0d-4a51-9c7e-2d8f6a1b4c90",
"status": "announced",
"deal": { "id": "7b1e5c2a-…", "status": "announced" },
"message": "Deal accepted. Both parties must confirm settlement before the registration transfers."
}The message says what accepting did: a consolidation opens its swap window, and a collab waits for its launch.
POST /api/v2/deals/{dealId}/respond does the same in one route: { "action": "accept" | "reject", "message": "…" }. Its response carries no deal.
Cancel a Deal
POST /api/v2/deals/{dealId}/cancelEither party may cancel until the burn lands. A cancelled deal cannot be reopened.
| Field | Type | Required | Description |
|---|---|---|---|
wallet_address | string | No | The wallet you are a party as. Defaults to your session's wallet |
json
{
"success": true,
"dealId": "7b1e5c2a-3f0d-4a51-9c7e-2d8f6a1b4c90",
"status": "cancelled",
"message": "Deal cancelled successfully."
}Offer Settings
GET /api/v2/deals/offer-floor
PUT /api/v2/deals/offer-floorWhat you are willing to be offered, across every registration you hold.
| Field | Type | Description |
|---|---|---|
floorUsd | integer | Smallest priced offer you will see, in whole dollars, up to 100,000,000. 0 or null clears it |
offersDisabled | boolean | Refuse every offer, of every type |
GET answers { "offerFloorUsd", "offersDisabled" }, and so does PUT with what it saved.
Webhooks
Deal events can be pushed to you instead of polled: deal.created, deal.accepted, deal.rejected, deal.cancelled, deal.completed, deal.failed. Each payload names the deal and carries no terms. See Webhooks.
Error Responses
Most errors answer { "error": "…", "code": "…" }.
| Status | Code | Description |
|---|---|---|
400 | VALIDATION_INVALID_INPUT | A missing or invalid field, an amount below the minimum, or an unpriceable currency |
400 | DEAL_INVALID_STATE | The deal cannot move that way from where it is |
401 | No session, or the session expired | |
402 | VALIDATION_INVALID_INPUT | No passing funds check covers the offer |
403 | A wallet not on your session, a holder who is not taking offers, or not a party to the deal | |
404 | VALIDATION_INVALID_INPUT | Deal not found |
409 | DEAL_CONFLICT | A swap window is already open on that registration |
429 | VALIDATION_INVALID_INPUT | Over your weekly offers or your standing offer value, or over the request rate |
503 | PROVIDER_UNAVAILABLE | Deals, your plan, or the offer lock could not be read. Try again |