> For the complete documentation index, see [llms.txt](https://docs.dutchmanbridge.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.dutchmanbridge.com/get-started/quickstart.md).

# Quickstart

This guide takes you from an empty account to a customer who has been verified, funded, and has moved money, with a `curl` call and the SDK equivalent for every step. It assumes you have an API key: see [Get started](/get-started/readme.md).

The examples use two shell variables:

```bash
export BASE="https://app.dutchmanbridge.com/api/v1/partner/v1"
export API_KEY="db_live_5f8c2a7e91d34b0c8a6e1f9d2b7c4e3a"
```

## The golden path

The customer id you get in step 3.1 goes into the path of every later call (`/customers/{customerId}/...`). Never put `customerId` in a request body — it is ignored.

### 1. Register a customer

`POST /partner/v1/customers` — the customer never logs into Dutchman Bridge; this only attributes an id to your account. `phoneNumber` is E.164, `country` is ISO alpha-2 and decides the customer's fiat currency.

```bash
curl -X POST "$BASE/customers" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Adaeze",
    "lastName": "Okonkwo",
    "email": "adaeze.okonkwo@example.com",
    "phoneNumber": "+2348012345678",
    "country": "NG"
  }'
```

```json
{
  "success": true,
  "data": {
    "id": "c0a8012e-7d1b-4f3a-9c2e-5b6d8e9f0a1b",
    "firstName": "Adaeze",
    "lastName": "Okonkwo",
    "middleName": null,
    "email": "adaeze.okonkwo@example.com",
    "phoneNumber": "+2348012345678",
    "country": "NG",
    "verifiedAt": null,
    "createdAt": "2026-09-10T13:00:00.000Z",
    "updatedAt": "2026-09-10T13:00:00.000Z"
  }
}
```

```ts
const customer = await bridge.customers.register({
  firstName: 'Adaeze',
  lastName: 'Okonkwo',
  email: 'adaeze.okonkwo@example.com',
  phoneNumber: '+2348012345678',
  country: 'NG',
});
const customerId = customer.id;
```

Store `data.id` — it is the `customerId` for everything that follows. A `customer.registered` webhook fires as well.

### 2. Submit Tier 1 KYC

`POST /partner/v1/customers/{customerId}/kyc/tier1` with both the customer's `bvn` and `nin` (11 digits each). Bridge verifies them independently against the issuing registries and name-matches them to the profile — passing your own check is not enough. Both numbers must resolve for auto-approval.

```bash
curl -X POST "$BASE/customers/$CUSTOMER_ID/kyc/tier1" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "bvn": "22345678901", "nin": "31234567890" }'
```

```json
{
  "success": true,
  "data": {
    "status": "REVIEW",
    "record": {
      "id": "5e2f7a1c-93b4-4d6e-8f0a-1b2c3d4e5f60",
      "customerId": "c0a8012e-7d1b-4f3a-9c2e-5b6d8e9f0a1b",
      "level": 1,
      "status": "REVIEW",
      "idType": "BVN",
      "rejectionReason": null,
      "createdAt": "2026-09-10T13:01:12.000Z"
    }
  }
}
```

```ts
const submission = await bridge.kyc.submitTier1(customerId, {
  bvn: '22345678901',
  nin: '31234567890',
});
// submission.status is 'APPROVED' or 'REVIEW'
```

`status` is `APPROVED` when the checks cleared immediately. It is `REVIEW` when a reviewer has to decide — a partial name match, a registry timeout, and similar. **Do not poll.** Wait for the [`kyc.status_updated`](https://docs.dutchmanbridge.com/developer-platform/webhooks#kyc) webhook, which carries the final `APPROVED` or `REJECTED` (with a `reason`). `GET .../kyc` returns the records if you need the current state on demand. Some rails are gated on an approved Tier 1 (an NGN virtual account, for one), so finish this step before funding.

#### Check where a customer stands

`GET /partner/v1/customers/{customerId}/verification` is the recommended single read for a customer's verification state — use it instead of combining `GET .../kyc` with `GET .../kyc/reverification`. It reports the highest approved tier, each tier's status, and exactly what to submit next.

```bash
curl "$BASE/customers/$CUSTOMER_ID/verification" -H "x-api-key: $API_KEY"
```

```json
{
  "success": true,
  "data": {
    "currentTier": 1,
    "tiers": {
      "1": {
        "status": "APPROVED",
        "updatedAt": "2026-09-10T13:01:12.000Z",
        "rejectionReason": null
      },
      "2": {
        "status": "NOT_STARTED",
        "updatedAt": null,
        "rejectionReason": null
      },
      "3": {
        "status": "NOT_STARTED",
        "updatedAt": null,
        "rejectionReason": null
      }
    },
    "nextRequired": ["ID_DOCUMENT", "SELFIE"],
    "reverification": null,
    "records": [
      {
        "id": "5e2f7a1c-93b4-4d6e-8f0a-1b2c3d4e5f60",
        "customerId": "c0a8012e-7d1b-4f3a-9c2e-5b6d8e9f0a1b",
        "level": 1,
        "status": "APPROVED",
        "idType": "BVN",
        "rejectionReason": null,
        "createdAt": "2026-09-10T13:01:12.000Z"
      }
    ]
  }
}
```

* `currentTier` is the highest tier with status `APPROVED` (`0` when none is).
* Each tier's `status` is `NOT_STARTED`, `PENDING`, `REVIEW`, `APPROVED` or `REJECTED` (with `rejectionReason`). Tiers 1 and 2 reflect the newest submission for that tier; tier 3 reflects the cross-border verification partners' decision.
* `nextRequired` names the inputs to collect next: `BVN` and `NIN` for `POST .../kyc/tier1`, `ID_DOCUMENT` and `SELFIE` for `POST .../kyc/tier2`, `DATE_OF_BIRTH` and `ADDRESS` for `POST .../kyc/tier3`. It is empty while the next tier is `PENDING` or in `REVIEW` — wait for `kyc.status_updated` — and once every tier is approved. If an admin has an open re-verification request, its `items` (which can also include `COUNTRY` or `ADDRESS_PROOF`) come first, and the request itself is in `reverification`.

#### International verification and its fee

Sending abroad needs the customer verified by our cross-border payout partner: `POST /partner/v1/customers/{customerId}/kyc/tier3`. Read `GET .../global-send/international-status` first. It says whether the customer is `eligible` (Tier 2 approved, or KYB approved for a business), which submission to send (`customerType`), and, on each of the `groups` (`americas_europe`, `china`), that group's `registrationFee`:

```json
"groups": [
  { "key": "china", "status": "NOT_STARTED", "countries": ["CN"],
    "registrationFee": { "amount": "3", "currency": "USDT", "paidBy": "CUSTOMER", "paid": false } }
]
```

* Each group verifies the customer separately, so the fee is charged once per customer **per group**, when that group's payout partner creates them as its customer on the first submission. By default it is **3 USDT for an individual, 11 USDT for a business**, but a group can be priced differently: always read the amount from the group. A first submission the partner refuses to create costs nothing. The top-level `registrationFee` is deprecated; it repeats the `americas_europe` group's fee for older clients. Once taken, the fee is non-refundable, and a re-submission after a refusal is free.
* It comes from the customer's USDT balance or, if that is short, the equivalent in their local currency at the live rate. The balance is checked before anything is sent: if neither covers the fee, the submission is refused with `INTERNATIONAL_FEE_INSUFFICIENT_BALANCE` and nothing is sent.
* Each group also lists `optedInCountries`: the destinations the customer has chosen. Choosing another country in a group that is already `APPROVED` (`POST .../kyc/tier3` with that `destinationCountry`) only records the choice: nothing goes to review and no fee is charged. A first send to a country records it too.
* If a group's partner asks again for details the customer already gave (address, document, selfie), `POST .../global-send/international-resend?group=china` sends what we hold to the same enrolment, free. It answers as international-status. A group that cannot be re-sent this way answers 400: the customer re-submits `POST .../kyc/tier3` instead.
* To send an unapproved enrolment back to the customer to fix (say, a postal code only they can give): `POST .../global-send/international-reject?group=china` with `{ "reason": "…" }`, worded for the customer. The group then reads `NEEDS_ATTENTION` with that `reason` and no `action`, so show them the form to re-submit `POST .../kyc/tier3`; re-submitting clears it.
* To pay it for your customers from your own balance: `PUT /partner/v1/fees/international-verification` with `{ "paysForCustomers": true }`. `paidBy` then reads `PARTNER`. The response lists each group's amounts under `groups`.

A **business** customer (`customerType: "BUSINESS"`) sends a `business` object with its submission: `businessType`, `description`, `accountPurpose`, `sourceOfFunds`, a `formationDocumentUrl`, and `associatedPersons`. Each person is an owner (25%+) or a control person, with their date of birth, address and ID. At least one person must control the business (give their `title`) and one must be able to authorise payments. Upload each document with `POST .../kyc/documents` and pass the returned `url`. Also include an `ownershipDocumentUrl`, unless a control person confirms the ownership with `attestOwnershipStructure: true`. A business with no website on its profile also needs a `natureOfBusinessDocumentUrl`. The registered name, number and address come from the business profile. When a refused business re-submits, it may omit `business`, and what it sent last time is used.

Each corridor in `GET .../global-send/corridors` carries a `region` (`africa`, `asia`, `europe`, `middle_east`, `north_america`, `south_america`, `oceania`), so you can list destinations grouped by continent and let customers search by it.

Some corridors describe their recipient form as data: `recipientSchema`, one entry per field (`key`, `label`, `required`, and optionally `pattern`, `patternHint`, `options`, `keyboard`, `placeholder`). Render it, send each value as `beneficiary[key]` (for example `phone` for the African corridors), and a recipient that does not match is refused before anything is debited.

#### Paying a supplier in China

China payouts are trade payments: the payout partner refuses one without the details below, whatever the reason given. `GET .../global-send/corridors` lists them on the China corridor (`recipientFields`, `requiredDocuments`, `allowedPurposes`, `minAssetAmount`), and a send missing any is refused before anything is debited.

* `purpose` must be `GOODS_PAYMENT`.
* On `beneficiary`: `bankCode` (the bank's 12-digit CNAPS code), `nameInLocalLanguage` (the recipient's name in Chinese characters), `nationalIdNumber` (their 18-character resident ID), and `logisticsTracking` (at least one `{ trackingNumber, carrierName, carrierCode? }`).
* `beneficiary.documents`: an `invoice` and a `contract`, each `{ type, url }` with the `url` from `POST .../kyc/documents` (JPEG, PNG or PDF, 5 MB at most).
* The send must come to at least `minAssetAmount` (26 USDT) after fees.

### 3. Fund the customer

Two ways to put fiat on the balance. Both credit the customer's local currency (from `country`) and emit `funding.received` when the money lands.

**Virtual account** — `GET /partner/v1/customers/{customerId}/virtual-account`. A dedicated bank account in the customer's name, issued on first read and identical on every read after. Anything transferred into it is credited. For NGN it is only issued once Tier 1 is approved.

```bash
curl "$BASE/customers/$CUSTOMER_ID/virtual-account" -H "x-api-key: $API_KEY"
```

```json
{
  "success": true,
  "data": {
    "accountNumber": "9012345678",
    "accountName": "Adaeze Okonkwo",
    "bankName": "Example Microfinance Bank",
    "bankCode": "090123",
    "currency": "NGN",
    "status": "active"
  }
}
```

```ts
const account = await bridge.wallet.getVirtualAccount(customerId);
```

**Funding charge** — `POST /partner/v1/customers/{customerId}/funding/charge` with an `amount` in the customer's fiat. Returns either a one-off bank account to pay into (`type: "bank_transfer"`, use `narration` as the transfer reference) or a `redirectUrl` the customer authorises the payment on (`type: "eft"`). It expires at `expiresAt`.

```bash
curl -X POST "$BASE/customers/$CUSTOMER_ID/funding/charge" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 150000 }'
```

```json
{
  "success": true,
  "data": {
    "id": "8d4b2f6a-1c3e-4a5b-9d7f-0e1a2b3c4d5e",
    "reference": "fund_3f2a9c1e",
    "type": "bank_transfer",
    "currency": "NGN",
    "amount": 150000,
    "status": "PENDING",
    "accountNumber": "7001234567",
    "accountName": "Dutchman Bridge / fund_3f2a9c1e",
    "bankName": "Example Bank",
    "bankCode": "000123",
    "narration": "fund_3f2a9c1e",
    "expiresAt": "2026-09-10T14:05:00.000Z",
    "redirectUrl": null,
    "createdAt": "2026-09-10T13:05:00.000Z"
  }
}
```

```ts
const charge = await bridge.wallet.createFundingCharge(customerId, {
  amount: 150000,
});
```

Crypto deposits work too: `GET .../addresses` lists the customer's deposit addresses per network, and a confirmed USDT/USDC deposit emits `deposit.received`.

### 4. Check the balance

`GET /partner/v1/customers/{customerId}/balance` — one entry per currency the customer holds.

```bash
curl "$BASE/customers/$CUSTOMER_ID/balance" -H "x-api-key: $API_KEY"
```

```json
{
  "success": true,
  "data": [
    { "currency": "NGN", "balance": 150000 },
    { "currency": "USDT", "balance": 0 }
  ]
}
```

```ts
const balances = await bridge.wallet.getBalance(customerId);
```

`GET .../transactions` is the paginated ledger behind these numbers; `GET .../balance/stream` is an SSE feed if you want live updates instead of polling.

### 5. Buy or sell stablecoin

Both directions are quote-then-execute. Get a quote, show it to the customer, and execute it before `expiresIn` seconds elapse — an expired quote is rejected and you must re-quote. Buying converts fiat balance to USDT; selling converts USDT balance to fiat. The quote request is the same shape either way: `type` is `"buy"` or `"sell"`. Neither direction touches a chain (both move balances), so no gas is charged; the only sell-side fee is the exchange's fixed execution fee, and buys are fee-free.

**Buy**

```bash
curl -X POST "$BASE/customers/$CUSTOMER_ID/buy/quote" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 100000,
    "sourceCurrency": "NGN",
    "destinationCurrency": "USDT",
    "type": "buy"
  }'
```

```json
{
  "success": true,
  "data": {
    "id": "q_7b2e4d9f1a3c",
    "sourceAmount": 100000,
    "sourceCurrency": "NGN",
    "destinationAmount": 61.35,
    "destinationCurrency": "USDT",
    "rate": 1630,
    "exchangeFee": 0,
    "expiresIn": 60
  }
}
```

```bash
curl -X POST "$BASE/customers/$CUSTOMER_ID/buy/execute" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "quoteId": "q_7b2e4d9f1a3c" }'
```

```json
{
  "success": true,
  "data": {
    "reference": "buy_0a9b3c7d",
    "fiatCurrency": "NGN",
    "fiatSpent": 100000,
    "usdtCredited": 61.35
  }
}
```

**Sell**

```bash
curl -X POST "$BASE/customers/$CUSTOMER_ID/sell/quote" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 25,
    "sourceCurrency": "USDT",
    "destinationCurrency": "NGN",
    "type": "sell"
  }'
```

```json
{
  "success": true,
  "data": {
    "id": "q_2c8f1e5a7d4b",
    "sourceAmount": 25,
    "sourceCurrency": "USDT",
    "destinationAmount": 40150,
    "destinationCurrency": "NGN",
    "rate": 1610,
    "exchangeFee": 100,
    "expiresIn": 60
  }
}
```

```bash
curl -X POST "$BASE/customers/$CUSTOMER_ID/sell/execute" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "quoteId": "q_2c8f1e5a7d4b" }'
```

```json
{
  "success": true,
  "data": {
    "reference": "sale_4c1d8e2f",
    "usdtSold": 25,
    "fiatCurrency": "NGN",
    "fiatCredited": 40150
  }
}
```

```ts
const quote = await bridge.ramp.sellQuote(customerId, {
  amount: 25,
  sourceCurrency: 'USDT',
  destinationCurrency: 'NGN',
  type: 'sell',
  network: 'tron',
});
const sale = await bridge.ramp.sellExecute(customerId, { quoteId: quote.id });
// bridge.ramp.buyQuote / bridge.ramp.buyExecute mirror this for a buy
```

`destinationAmount` is net of every fee listed. A sell always lands on the customer's fiat balance; pay it out with a withdrawal (3.7) when you choose — the withdrawal fee is charged then, not here. `GET .../rate` gives indicative buy/sell rates without locking a quote. Both rates are all-in: the spread and fees are already folded in, so `amount * rate` is what the customer pays or receives. The fixed USDT exchange fee can only be spread over a known amount, so pass `?amount=25` (USDT) to have `sellRate` include it for that amount; without it, `sellRate` carries the spread only.

### 6. Send stablecoin on-chain

Three calls: list networks, quote, send.

`GET /partner/v1/customers/{customerId}/send/networks` — the chains the customer can send on, each with its flat fee, minimum, and supported assets. `recommended` marks the cheapest.

```bash
curl "$BASE/customers/$CUSTOMER_ID/send/networks" -H "x-api-key: $API_KEY"
```

```json
{
  "success": true,
  "data": [
    {
      "network": "tron",
      "displayName": "TRON",
      "evm": false,
      "flatFeeUsdt": 1,
      "minSendUsdt": 5,
      "recommended": true,
      "assets": ["USDT"]
    },
    {
      "network": "polygon",
      "displayName": "Polygon",
      "evm": true,
      "flatFeeUsdt": 1.5,
      "minSendUsdt": 5,
      "recommended": false,
      "assets": ["USDT", "USDC"]
    }
  ]
}
```

`POST .../send/quote` — checks the amount against the minimum and the balance without moving anything. Only proceed when `valid` is `true`.

```bash
curl -X POST "$BASE/customers/$CUSTOMER_ID/send/quote" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "asset": "USDT", "amount": 20, "network": "tron" }'
```

```json
{
  "success": true,
  "data": {
    "asset": "USDT",
    "network": "tron",
    "amount": 20,
    "fee": 1,
    "netAmount": 19,
    "minSendUsdt": 5,
    "balance": 61.35,
    "meetsMin": true,
    "sufficientBalance": true,
    "valid": true
  }
}
```

`POST .../send` — same body plus `toAddress`. Returns 201 as soon as the send is accepted; `txHash` fills in once it is broadcast.

```bash
curl -X POST "$BASE/customers/$CUSTOMER_ID/send" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "asset": "USDT",
    "amount": 20,
    "network": "tron",
    "toAddress": "TXk8rQSAvPvBBNtqSoY6nCfsXWCSSpTVQF"
  }'
```

```json
{
  "success": true,
  "data": {
    "reference": "csend_1b2c9d4e",
    "asset": "USDT",
    "network": "tron",
    "amount": 20,
    "fee": 1,
    "netAmount": 19,
    "toAddress": "TXk8rQSAvPvBBNtqSoY6nCfsXWCSSpTVQF",
    "txHash": null,
    "status": "PENDING"
  }
}
```

```ts
const networks = await bridge.send.listNetworks(customerId);
const sendQuote = await bridge.send.quote(customerId, {
  asset: 'USDT',
  amount: 20,
  network: 'tron',
});
if (sendQuote.valid) {
  const send = await bridge.send.send(customerId, {
    asset: 'USDT',
    amount: 20,
    network: 'tron',
    toAddress: 'TXk8rQSAvPvBBNtqSoY6nCfsXWCSSpTVQF',
  });
}
```

Track it through the `send.pending`, `send.confirmed` and `send.failed` webhooks. Every send opens with `send.pending` -- the exchange that custodies the funds broadcasts it, so there is no transaction hash at submission. On failure the reserved amount returns to the balance.

### 7. Withdraw fiat to a bank account

`POST /partner/v1/customers/{customerId}/withdraw` pays fiat balance to the customer's **default bank account**, so add one first: `GET .../banks?country=NG` for bank codes, `POST .../bank/verify-account` to resolve the account name, then `POST .../accounts` to save it (the first account becomes the default).

A withdrawal carries a fee, taken **out of** the amount: the balance is debited the amount you send, and the bank receives that less the fee. The fee is a percentage of the amount plus a fixed charge for the currency. In the response, `amount` is what the bank receives and `totalDebited` what left the balance (the amount you asked for). The bank must receive at least NGN 100 after the fee; a smaller withdrawal is refused before anything moves, with the smallest amount that works. Price it first so the customer can confirm:

```bash
curl "$BASE/customers/$CUSTOMER_ID/withdraw/quote?amount=40000" \
  -H "x-api-key: $API_KEY"
```

```json
{
  "success": true,
  "data": {
    "currency": "NGN",
    "amount": 39750,
    "fee": 250,
    "totalDebited": 40000,
    "feeBps": 50,
    "feeFlat": 50
  }
}
```

```bash
curl -X POST "$BASE/customers/$CUSTOMER_ID/withdraw" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 40000 }'
```

```json
{
  "success": true,
  "data": {
    "id": "b7e3d1f9-2a4c-4e6b-8d0f-1c2e3a4b5c6d",
    "reference": "wd_9d1c5e7a",
    "amount": "39750.00000000",
    "fee": "250.00000000",
    "totalDebited": "40000.00000000",
    "currency": "NGN",
    "status": "PROCESSING",
    "failureReason": null,
    "bankAccount": {
      "bankName": "Example Bank",
      "accountName": "Adaeze Okonkwo",
      "accountNumberLast4": "6789"
    },
    "createdAt": "2026-09-10T13:20:00.000Z",
    "updatedAt": "2026-09-10T13:20:00.000Z"
  }
}
```

```ts
const withdrawal = await bridge.wallet.withdraw(customerId, { amount: 40000 });
```

Note `amount`, `fee` and `totalDebited` come back as decimal strings. A withdrawal that fails or is reversed puts the whole `totalDebited` back on the balance, fee included. `status` is `PROCESSING` until the bank confirms. Completion arrives as `withdrawal.completed` (or `withdrawal.failed`, with the amount back on the balance). `GET .../withdrawals` is the paginated history.

### 8. Register your webhook

`PUT /partner/v1/webhook` — set the URL once. Bridge mints a signing secret, returns it **in this response only**, and immediately sends a signed `webhook.test` event so you can confirm the endpoint before anything real fires.

```bash
curl -X PUT "$BASE/webhook" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://api.example.com/dutchman/webhooks" }'
```

```json
{
  "success": true,
  "data": {
    "url": "https://api.example.com/dutchman/webhooks",
    "secret": "3f9a6c1e8b2d4f7a9c0e1b3d5f7a9c2e4b6d8f0a1c3e5b7d9f1a3c5e7b9d1f3a",
    "test": { "delivered": true, "statusCode": 200, "error": null }
  }
}
```

```ts
const webhook = await bridge.webhook.setUrl({
  url: 'https://api.example.com/dutchman/webhooks',
});
await saveSecret(webhook.secret); // shown once
```

Store `secret` immediately — it cannot be read back, only rotated with `POST .../webhook/rotate-secret`. Verifying the `X-Dutchman-Signature` header, the retry schedule, and the full event catalogue are in [Webhooks](https://docs.dutchmanbridge.com/developer-platform/webhooks).

When it works end to end, run through the [going-live checklist](/get-started/going-live.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.dutchmanbridge.com/get-started/quickstart.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
