# Wallet

Your balance and money history. Integrators also manage cards, top-ups and withdrawals here.

This page covers [the wallet object](https://public-api.dapi.digital/docs/reference/wallet.md#wallet_object) and [the transaction object](https://public-api.dapi.digital/docs/reference/wallet.md#transaction_object).

## Endpoints

-   [GET `/v2/wallet`](https://public-api.dapi.digital/docs/reference/wallet.md#get_wallet)
-   [GET `/v2/billing-details`](https://public-api.dapi.digital/docs/reference/wallet.md#get_billing_details)
-   [PUT `/v2/billing-details`](https://public-api.dapi.digital/docs/reference/wallet.md#save_billing_details)
-   [GET `/v2/cards`](https://public-api.dapi.digital/docs/reference/wallet.md#list_cards)
-   [POST `/v2/cards/setup-sessions`](https://public-api.dapi.digital/docs/reference/wallet.md#start_card_setup)
-   [POST `/v2/cards/setup-sessions/{session_id}/complete`](https://public-api.dapi.digital/docs/reference/wallet.md#complete_card_setup)
-   [POST `/v2/cards/{id}/make-default`](https://public-api.dapi.digital/docs/reference/wallet.md#make_card_default)
-   [DELETE `/v2/cards/{id}`](https://public-api.dapi.digital/docs/reference/wallet.md#remove_card)
-   [POST `/v2/top-ups`](https://public-api.dapi.digital/docs/reference/wallet.md#start_top_up)
-   [GET `/v2/wallet/settings`](https://public-api.dapi.digital/docs/reference/wallet.md#get_wallet_settings)
-   [PATCH `/v2/wallet/settings`](https://public-api.dapi.digital/docs/reference/wallet.md#update_wallet_settings)
-   [GET `/v2/withdrawals`](https://public-api.dapi.digital/docs/reference/wallet.md#list_withdrawals)
-   [POST `/v2/withdrawals`](https://public-api.dapi.digital/docs/reference/wallet.md#create_withdrawal)
-   [GET `/v2/withdrawals/eligibility`](https://public-api.dapi.digital/docs/reference/wallet.md#get_withdrawal_eligibility)
-   [POST `/v2/withdrawals/{id}/cancel`](https://public-api.dapi.digital/docs/reference/wallet.md#cancel_withdrawal)
-   [GET `/v2/payout-details`](https://public-api.dapi.digital/docs/reference/wallet.md#get_payout_details)
-   [PUT `/v2/payout-details`](https://public-api.dapi.digital/docs/reference/wallet.md#save_payout_details)
-   [GET `/v2/transactions`](https://public-api.dapi.digital/docs/reference/wallet.md#list_transactions)
-   [GET `/v2/transactions/summary`](https://public-api.dapi.digital/docs/reference/wallet.md#get_transaction_summary)

## The wallet object

Your 3DAPI balance. Top-ups, withdrawals and wallet settings are managed in the dashboard.

### Attributes

-   `object` (string): Names the type of this object. Always `wallet`.
-   `balance` (object): What you can spend: manual, imported and API orders and stock purchases are paid from it.
    -   `amount` (integer): Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.
    -   `currency` (string): Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.
-   `sample_credit` (object): Credit for sample orders: an order you pay from the wallet and ship to your own country (your billing or sign-up country). Spent only when the order asks for it (`use_sample_credit`), before the balance; cannot be withdrawn, and expires. Integrators get $15 once, when their account is approved, valid for 30 days.
    -   `amount` (integer): Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.
    -   `currency` (string): Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.
-   `sample_credit_expires_at` (timestamp or null): When the soonest-expiring part of `sample_credit` lapses. Null when there is none.
-   `pending_withdrawals` (object): Withdrawals you requested that are not paid out yet. Already taken off `balance`.
    -   `amount` (integer): Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.
    -   `currency` (string): Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.
-   `updated_at` (timestamp): When the balance last changed.

**The wallet object**

```json
{
  "object": "wallet",
  "balance": {
    "amount": 24310,
    "currency": "usd"
  },
  "sample_credit": {
    "amount": 2000,
    "currency": "usd"
  },
  "sample_credit_expires_at": "2026-11-13T08:02:11Z",
  "pending_withdrawals": {
    "amount": 0,
    "currency": "usd"
  },
  "updated_at": "2026-10-14T08:02:11Z"
}
```

## Get your wallet balance

GET `/v2/wallet`

- **Credentials**: An API key with the `wallet:read` scope (full access includes every scope). A signed-in dashboard session works too.

- **For**: Designers and Integrators

- **Operation id**: `get_wallet`

Returns your balance, sample credit and pending withdrawals. Check it before creating orders that are paid from the wallet. For designers and integrators.

### Returns 200 OK

The wallet. Returns [the wallet object](https://public-api.dapi.digital/docs/reference/wallet.md#wallet_object).

### Responses

-   `200`: The wallet.
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl "https://public-api.dapi.digital/v2/wallet" \
  -H "x-api-key: $DAPI_API_KEY"
```

## Get your billing details

GET `/v2/billing-details`

- **Credentials**: A signed-in dashboard session only; API keys are refused.

- **For**: Integrators

- **Operation id**: `get_billing_details`

Who your invoices are made out to. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).

### Returns 200 OK

Your billing details.

Response attributes (BillingDetails):

-   `object` (string): Names the type of this object. Always `billing_details`.
-   `legal_name` (string): Your company’s registered legal name, as it appears on invoices.
-   `country` (string): Country of registration, ISO 3166-1 alpha-2. It decides the VAT on top-ups and which of 3DAPI’s payment accounts you pay.
-   `address_line1` (string): First line of the registered address.
-   `address_line2` (string or null): Second line of the address. Null when there is none.
-   `city` (string): City of the registered address.
-   `postal_code` (string): Postal or ZIP code of the registered address.
-   `vat_number` (string or null): Your VAT number (EU) or VAT or tax number (UK), or null.
-   `vat_status` (VatStatus):
    
    What 3DAPI knows about `vat_number`:
    
    -   `none`: no number given.
    -   `validated`: an EU VAT number VIES confirmed, or 3DAPI approved after a manual check. Top-ups carry no VAT (reverse charge).
    -   `in_review`: VIES did not confirm the number, so 3DAPI is checking it by hand. Top-ups are charged VAT until it is approved.
    -   `invalid`: 3DAPI rejected the number after checking it. Top-ups are charged VAT; check the number and save again.
    -   `unverified`: VIES could not confirm the number and no manual check is open. Top-ups are charged VAT.
    -   `not_checked`: a number from outside the EU, which today means a UK VAT or tax number, kept for your invoices. VAT follows your country.
    
-   `required_tax_form` (string or null): The IRS form your country needs with every save: `w9` for US businesses; `w8ben_e` for businesses outside the US, EU and UK, which send either a W-8BEN (`w8ben`, a person or sole trader) or a W-8BEN-E (`w8ben_e`, a company or other entity). Null otherwise.
-   `tax_form` (object or null): The latest tax form on file. Its tax ids are never returned.
    -   `kind` (TaxFormKind): `w9`: IRS Form W-9 (US). `w8ben`: IRS Form W-8BEN (a person or sole trader outside the US). `w8ben_e`: IRS Form W-8BEN-E (a company or other entity outside the US).
    -   `submitted_at` (timestamp): Point in time, ISO 8601 in UTC.
-   `email` (string or null): Where invoices are addressed: your account email.
-   `updated_at` (timestamp or null): Point in time, ISO 8601 in UTC.

### Responses

-   `200`: Your billing details.
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `404`: You have not added billing details yet (`billing_details_not_found`).
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl "https://public-api.dapi.digital/v2/billing-details" \
  -H "x-api-key: $DAPI_API_KEY"
```

**Response 200**

```json
{
  "object": "billing_details",
  "legal_name": "Acme Prints Ltd",
  "country": "DE",
  "address_line1": "Hauptstrasse 12",
  "address_line2": null,
  "city": "Berlin",
  "postal_code": "10115",
  "vat_number": "DE123456789",
  "vat_status": "validated",
  "required_tax_form": null,
  "tax_form": null,
  "email": "billing@acme.example",
  "updated_at": "2026-10-21T09:30:00Z"
}
```

## Save your billing details

PUT `/v2/billing-details`

- **Credentials**: A signed-in dashboard session only; API keys are refused.

- **For**: Integrators

- **Operation id**: `save_billing_details`

Replaces your billing details. Required before adding a card or topping up.

An EU VAT number is checked with VIES: a confirmed number means top-ups carry no VAT (reverse charge). Outside the EU, VAT follows your country, as for every account.

Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).

### Request body

-   `legal_name` (string, required): Your company’s registered legal name, as it should appear on invoices.
-   `country` (string, required): ISO 3166-1 alpha-2.
-   `address_line1` (string, required): First line of the registered address.
-   `address_line2` (string or null): Second line of the address. Omit or null for none.
-   `city` (string, required): City of the registered address.
-   `postal_code` (string, required): Postal or ZIP code of the registered address.
-   `vat_number` (string or null): EU VAT number (checked with VIES when you save; one VIES does not confirm goes to a manual check), or a UK VAT or tax number. Refused for US businesses and businesses outside the US, EU and UK, whose tax ids go on the W-9, W-8BEN or W-8BEN-E instead (`validation_failed`). Omit or null for none.
-   `w9` (object or null): Required for a US business, on every save.
    -   `name` (string, required): Your name as shown on your federal income tax return.
    -   `business_name` (string or null): Business name or disregarded entity name, when it differs from `name`. Omit or null for none.
    -   `federal_tax_classification` (string, required):
        
        Your federal tax classification, as ticked on the form:
        
        -   `individual_sole_proprietor`: individual, sole proprietor or single-member LLC.
        -   `c_corporation`: C corporation.
        -   `s_corporation`: S corporation.
        -   `partnership`: partnership.
        -   `trust_estate`: trust or estate.
        -   `llc`: limited liability company; give its tax treatment in `llc_tax_classification`.
        -   `other`: describe it in `other_classification`.
        
    -   `llc_tax_classification` (string or null): Only when `federal_tax_classification` is `llc`: how the LLC is taxed. `C`: C corporation, `S`: S corporation, `P`: partnership. Ignored for other classifications.
    -   `other_classification` (string or null): Only when `federal_tax_classification` is `other`: the classification, in your words. Ignored for other classifications.
    -   `exempt_payee_code` (string or null): Exempt payee code from the form’s exemptions box, if any. Omit or null for none.
    -   `fatca_reporting_code` (string or null): Exemption from FATCA reporting code, if any. Omit or null for none.
    -   `address` (string, required): Street address: number, street, and apartment or suite.
    -   `city` (string, required): City of `address`.
    -   `state` (string, required): State of `address`.
    -   `zip_code` (string, required): ZIP code of `address`.
    -   `account_numbers` (string or null): Account number(s) to list on the form, if any. Omit or null for none.
    -   `tin` (string, required): Your taxpayer identification number, of the kind in `tin_type`: exactly 9 digits. Dashes and spaces are allowed and removed before it is stored. Never returned by the API.
    -   `tin_type` (string, required): Which number `tin` is. `ssn`: Social Security Number. `ein`: Employer Identification Number.
    -   `signature_name` (string, required): Your full name, typed as your signature. The IP address and user agent of the request are recorded with the form.
    -   `certified` (boolean, required): You certify the form under penalties of perjury, as on the IRS form.
-   `w8ben` (object or null): For a business outside the US, EU and UK that is a person or sole trader: required on every save, instead of `w8ben_e`. Send one of the two, never both.
    -   `name` (string, required): Name of the individual who is the beneficial owner of the income: you, or the owner of your sole trade.
    -   `country_of_citizenship` (string, required): Country of citizenship, ISO 3166-1 alpha-2.
    -   `residence_address` (string, required): Permanent residence address: street and number.
    -   `residence_city` (string, required): City of `residence_address`.
    -   `residence_state` (string or null): State or province of `residence_address`, if any. Omit or null for none.
    -   `residence_postal_code` (string, required): Postal code of `residence_address`.
    -   `residence_country` (string, required): Country of `residence_address`, ISO 3166-1 alpha-2.
    -   `mailing_address` (string or null): Mailing address, when different from the residence address. Omit or null for none.
    -   `us_tin` (string or null): US taxpayer identification number (SSN or ITIN), if you have one. Omit or null for none. Never returned by the API.
    -   `foreign_tin` (string or null): Tax identifying number in your country of residence. Required unless `foreign_tin_not_required` is true. Never returned by the API.
    -   `foreign_tin_not_required` (boolean): True when your country does not legally require a foreign tax identifying number. Default false.
    -   `reference_number` (string or null): Reference number(s) to show on the form, if any. Omit or null for none.
    -   `date_of_birth` (string, required): Date of birth, YYYY-MM-DD.
    -   `treaty_country` (string or null): Country, ISO 3166-1 alpha-2, whose income tax treaty with the US you claim benefits under. Omit or null when you claim none.
    -   `treaty_article` (string or null): With `treaty_country`: the article and paragraph of the treaty you claim under.
    -   `treaty_rate` (string or null): With `treaty_country`: the rate of withholding you claim.
    -   `treaty_income_type` (string or null): With `treaty_country`: the type of income.
    -   `signature_name` (string, required): Full name of the person signing, typed as the signature. The IP address and user agent of the request are recorded with the form.
    -   `capacity` (string or null): Capacity in which the signer acts, when not the beneficial owner. Omit or null otherwise.
    -   `certified` (boolean, required): You certify the form under penalties of perjury, as on the IRS form.
-   `w8ben_e` (object or null): For a business outside the US, EU and UK that is a company or other entity: required on every save, instead of `w8ben`. Send one of the two, never both.
    -   `name` (string, required): Name of the organization that is the beneficial owner of the income.
    -   `country_of_incorporation` (string, required): Country where the organization is incorporated or organized, ISO 3166-1 alpha-2.
    -   `disregarded_entity_name` (string or null): Name of the disregarded entity, if one receives the payment. Omit or null for none.
    -   `chapter3_status` (string, required): Chapter 3 status (entity type), as on the dashboard form. `central_bank` is a central bank of issue.
        
        Possible values: `corporation`, `disregarded_entity`, `partnership`, `simple_trust`, `grantor_trust`, `complex_trust`, `estate`, `government`, `central_bank`, `tax_exempt_org`, `private_foundation`, `international_org`, `other`.
        
    -   `chapter4_status` (string, required): Chapter 4 (FATCA) status, as on the dashboard form. With `other`, describe it in `chapter4_status_other`.
        
        Possible values: `active_nffe`, `passive_nffe`, `participating_ffi`, `reporting_model1_ffi`, `reporting_model2_ffi`, `nonparticipating_ffi`, `exempt_beneficial_owner`, `certified_deemed_compliant_ffi`, `other`.
        
    -   `chapter4_status_other` (string or null): Only when `chapter4_status` is `other`: the FATCA status, in your words. Ignored otherwise.
    -   `residence_address` (string, required): Permanent residence address of the organization: street and number.
    -   `residence_city` (string, required): City of `residence_address`.
    -   `residence_state` (string or null): State or province of `residence_address`, if any. Omit or null for none.
    -   `residence_postal_code` (string, required): Postal code of `residence_address`.
    -   `residence_country` (string, required): Country of `residence_address`, ISO 3166-1 alpha-2.
    -   `us_tin` (string or null): US taxpayer identification number, if the form requires one for you. Omit or null for none. Never returned by the API.
    -   `giin` (string or null): Global Intermediary Identification Number (GIIN), if you have one. Omit or null for none.
    -   `foreign_tin` (string or null): Tax identifying number in your country of residence, if any. Omit or null for none. Never returned by the API.
    -   `reference_number` (string or null): Reference number(s) to show on the form, if any. Omit or null for none.
    -   `treaty_country` (string or null): Country, ISO 3166-1 alpha-2, whose income tax treaty with the US you claim benefits under. Omit or null when you claim none.
    -   `signature_name` (string, required): Full name of the person signing, typed as the signature. The IP address and user agent of the request are recorded with the form.
    -   `capacity` (string or null): Capacity in which the signer acts for the organization, if given.
    -   `certified` (boolean, required): You certify the form under penalties of perjury, as on the IRS form.

### Returns 200 OK

The saved details, with the VAT check result.

Response attributes (BillingDetails):

-   `object` (string): Names the type of this object. Always `billing_details`.
-   `legal_name` (string): Your company’s registered legal name, as it appears on invoices.
-   `country` (string): Country of registration, ISO 3166-1 alpha-2. It decides the VAT on top-ups and which of 3DAPI’s payment accounts you pay.
-   `address_line1` (string): First line of the registered address.
-   `address_line2` (string or null): Second line of the address. Null when there is none.
-   `city` (string): City of the registered address.
-   `postal_code` (string): Postal or ZIP code of the registered address.
-   `vat_number` (string or null): Your VAT number (EU) or VAT or tax number (UK), or null.
-   `vat_status` (VatStatus): Same shape as `VatStatus` above.
-   `required_tax_form` (string or null): The IRS form your country needs with every save: `w9` for US businesses; `w8ben_e` for businesses outside the US, EU and UK, which send either a W-8BEN (`w8ben`, a person or sole trader) or a W-8BEN-E (`w8ben_e`, a company or other entity). Null otherwise.
-   `tax_form` (object or null): The latest tax form on file. Its tax ids are never returned.
    -   `kind` (TaxFormKind): Same shape as `TaxFormKind` above.
    -   `submitted_at` (timestamp): Point in time, ISO 8601 in UTC.
-   `email` (string or null): Where invoices are addressed: your account email.
-   `updated_at` (timestamp or null): Point in time, ISO 8601 in UTC.

### Responses

-   `200`: The saved details, with the VAT check result.
-   `400`: The request is invalid (`validation_failed`, `details.errors` per field): for example a W-9 `tin` without exactly 9 digits, `llc_tax_classification` or `other_classification` missing for that classification, `chapter4_status_other` missing when `chapter4_status` is `other`, or a `vat_number` from a US business or a business outside the US, EU and UK.
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `422`: A US business without `w9`, or a business outside the US, EU and UK without `w8ben` or `w8ben_e` (`tax_form_required`, `details.required`, `details.accepted`).
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl -X PUT "https://public-api.dapi.digital/v2/billing-details" \
  -H "x-api-key: $DAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "legal_name": "Acme Prints Ltd",
  "country": "DE",
  "address_line1": "Hauptstrasse 12",
  "city": "Berlin",
  "postal_code": "10115",
  "vat_number": "DE123456789"
}'
```

**Response 200**

```json
{
  "object": "billing_details",
  "legal_name": "Acme Prints Ltd",
  "country": "DE",
  "address_line1": "Hauptstrasse 12",
  "address_line2": null,
  "city": "Berlin",
  "postal_code": "10115",
  "vat_number": "DE123456789",
  "vat_status": "validated",
  "required_tax_form": null,
  "tax_form": null,
  "email": "billing@acme.example",
  "updated_at": "2026-10-21T09:30:00Z"
}
```

## List saved cards

GET `/v2/cards`

- **Credentials**: A signed-in dashboard session only; API keys are refused.

- **For**: Integrators

- **Operation id**: `list_cards`

Your saved cards, the default first. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).

### Returns 200 OK

Your cards, all on one page.

Response attributes (CardList):

-   `object` (string): Names the type of this object. Always `list`.
-   `data` (array of Card): The page of results.
    -   `id` (string): Unique identifier of the card, prefixed with `crd_`. Treat it as an opaque string: store it and compare it, never parse it.
    -   `object` (string): Names the type of this object. Always `card`.
    -   `brand` (string or null): Card brand as Stripe reports it, lowercase. Null when it is not known.
    -   `last4` (string or null): Last 4 digits of the card number. Null when it is not known.
    -   `exp_month` (integer or null): Expiry month, 1 to 12. Null when it is not known.
    -   `exp_year` (integer or null): Expiry year, four digits. Null when it is not known.
    -   `is_default` (boolean): The card top-ups and auto top up charge. Exactly one of your cards is the default.
    -   `created_at` (timestamp): Point in time, ISO 8601 in UTC.
-   `has_more` (boolean): True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`.
-   `next_cursor` (string or null): Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.

### Responses

-   `200`: Your cards, all on one page.
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl "https://public-api.dapi.digital/v2/cards" \
  -H "x-api-key: $DAPI_API_KEY"
```

**Response 200**

```json
{
  "object": "list",
  "data": [
    {
      "id": "crd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
      "object": "card",
      "brand": "visa",
      "last4": "4242",
      "exp_month": 12,
      "exp_year": 2030,
      "is_default": true,
      "created_at": "2026-10-21T09:30:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

## Start adding a card

POST `/v2/cards/setup-sessions`

- **Credentials**: A signed-in dashboard session only; API keys are refused.

- **For**: Integrators

- **Operation id**: `start_card_setup`

Opens a Stripe page that saves a card. Stripe sends the browser back to the wallet page with `session_id`; call `complete_card_setup` with it. The new card becomes the default. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).

### Returns 201 Created

The Stripe page to open.

Response attributes (CheckoutSession):

-   `object` (string): Names the type of this object. Always `checkout_session`.
-   `session_id` (string): Stripe’s id for the page. For a card setup, pass it to `complete_card_setup` after the redirect back.
-   `url` (string): The Stripe-hosted page to send the browser to.

### Responses

-   `201`: The Stripe page to open.
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `409`: Add billing details first (`billing_details_required`).
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user), plus 60 per minute per API key (or signed-in user) for quote, upload, order and payment operations. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.
-   `503`: Card payments are not available on this deployment (`card_payments_unavailable`).

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl -X POST "https://public-api.dapi.digital/v2/cards/setup-sessions" \
  -H "x-api-key: $DAPI_API_KEY"
```

**Response 201**

```json
{
  "object": "checkout_session",
  "session_id": "cs_test_a1b2c3",
  "url": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3"
}
```

## Finish adding a card

POST `/v2/cards/setup-sessions/{session_id}/complete`

- **Credentials**: A signed-in dashboard session only; API keys are refused.

- **For**: Integrators

- **Operation id**: `complete_card_setup`

Saves the card from a finished setup page right away, without waiting for Stripe's notification. Safe to call more than once. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).

### Path parameters

-   `session_id` (string, required)

### Returns 200 OK

Your cards, the new one included.

Response attributes (CardList):

-   `object` (string): Names the type of this object. Always `list`.
-   `data` (array of Card): The page of results. Same shape as `Card` above.
-   `has_more` (boolean): True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`.
-   `next_cursor` (string or null): Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.

### Responses

-   `200`: Your cards, the new one included.
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `404`: No card setup with this id was started from your account (`card_setup_not_found`).
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.
-   `503`: Card payments are not available right now (`card_payments_unavailable`). Try again later.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl -X POST "https://public-api.dapi.digital/v2/cards/setup-sessions/string/complete" \
  -H "x-api-key: $DAPI_API_KEY"
```

**Response 200**

```json
{
  "object": "list",
  "data": [
    {
      "id": "crd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
      "object": "card",
      "brand": "visa",
      "last4": "4242",
      "exp_month": 12,
      "exp_year": 2030,
      "is_default": true,
      "created_at": "2026-10-21T09:30:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

## Make a card the default

POST `/v2/cards/{id}/make-default`

- **Credentials**: A signed-in dashboard session only; API keys are refused.

- **For**: Integrators

- **Operation id**: `make_card_default`

The default card pays for top-ups and auto top up. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).

### Path parameters

-   `id` (string, required): Unique identifier of the card, prefixed with `crd_`. Treat it as an opaque string: store it and compare it, never parse it.

### Returns 200 OK

The card, now the default.

Response attributes (Card):

-   `id` (string): Unique identifier of the card, prefixed with `crd_`. Treat it as an opaque string: store it and compare it, never parse it.
-   `object` (string): Names the type of this object. Always `card`.
-   `brand` (string or null): Card brand as Stripe reports it, lowercase. Null when it is not known.
-   `last4` (string or null): Last 4 digits of the card number. Null when it is not known.
-   `exp_month` (integer or null): Expiry month, 1 to 12. Null when it is not known.
-   `exp_year` (integer or null): Expiry year, four digits. Null when it is not known.
-   `is_default` (boolean): The card top-ups and auto top up charge. Exactly one of your cards is the default.
-   `created_at` (timestamp): Point in time, ISO 8601 in UTC.

### Responses

-   `200`: The card, now the default.
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `404`: No such card (`card_not_found`).
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl -X POST "https://public-api.dapi.digital/v2/cards/crd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu/make-default" \
  -H "x-api-key: $DAPI_API_KEY"
```

**Response 200**

```json
{
  "id": "crd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
  "object": "card",
  "brand": "visa",
  "last4": "4242",
  "exp_month": 12,
  "exp_year": 2030,
  "is_default": true,
  "created_at": "2026-10-21T09:30:00Z"
}
```

## Remove a card

DELETE `/v2/cards/{id}`

- **Credentials**: A signed-in dashboard session only; API keys are refused.

- **For**: Integrators

- **Operation id**: `remove_card`

Removes the card from your wallet and from Stripe. A removed default hands the default to your newest other card. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).

### Path parameters

-   `id` (string, required): Unique identifier of the card, prefixed with `crd_`. Treat it as an opaque string: store it and compare it, never parse it.

### Returns 200 OK

The card was removed.

Response attributes (CardDeleted):

-   `id` (string): Unique identifier of the card, prefixed with `crd_`. Treat it as an opaque string: store it and compare it, never parse it.
-   `object` (string): Names the type of this object. Always `card`.
-   `deleted` (boolean): Always true: the card was removed.

### Responses

-   `200`: The card was removed.
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `404`: No such card (`card_not_found`).
-   `409`: It is your only card and auto top up is on (`auto_top_up_needs_card`). Turn auto top up off first.
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.
-   `503`: Card payments are not available right now (`card_payments_unavailable`). Try again later. Stripe refused to remove the card (`card_removal_failed`); try again in a moment.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl -X DELETE "https://public-api.dapi.digital/v2/cards/crd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu" \
  -H "x-api-key: $DAPI_API_KEY"
```

**Response 200**

```json
{
  "id": "crd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
  "object": "card",
  "deleted": true
}
```

## Top up your wallet

POST `/v2/top-ups`

- **Credentials**: A signed-in dashboard session only; API keys are refused.

- **For**: Integrators

- **Operation id**: `start_top_up`

Opens a Stripe page that charges a card for `amount`, plus VAT when your billing details call for it (EU and UK businesses without a confirmed VAT number). Only `amount` lands in your wallet, once Stripe confirms the payment; a Stripe invoice is issued for the full charge.

You need billing details and a saved card first.

Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).

### Request body

-   `amount` (object, required): What lands in your wallet, $1.00 to $100,000.00. When VAT applies it is added on top and shown on the Stripe page.
    -   `amount` (integer, required): Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.
    -   `currency` (string, required): Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.

### Returns 201 Created

The Stripe page to open.

Response attributes (CheckoutSession):

-   `object` (string): Names the type of this object. Always `checkout_session`.
-   `session_id` (string): Stripe’s id for the page. For a card setup, pass it to `complete_card_setup` after the redirect back.
-   `url` (string): The Stripe-hosted page to send the browser to.

### Responses

-   `201`: The Stripe page to open.
-   `400`: The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `409`: Add billing details first (`billing_details_required`), or add a card (`no_saved_card`).
-   `422`: The amount is outside $1.00 to $100,000.00 (`amount_out_of_range`).
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user), plus 60 per minute per API key (or signed-in user) for quote, upload, order and payment operations. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.
-   `503`: Card payments are not available right now (`card_payments_unavailable`). Try again later.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl -X POST "https://public-api.dapi.digital/v2/top-ups" \
  -H "x-api-key: $DAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "amount": {
    "amount": 5000,
    "currency": "usd"
  }
}'
```

**Response 201**

```json
{
  "object": "checkout_session",
  "session_id": "cs_test_a1b2c3",
  "url": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3"
}
```

## Get wallet settings

GET `/v2/wallet/settings`

- **Credentials**: A signed-in dashboard session only; API keys are refused.

- **For**: Integrators

- **Operation id**: `get_wallet_settings`

Auto top up. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).

### Returns 200 OK

Your settings.

Response attributes (WalletSettings):

-   `object` (string): Names the type of this object. Always `wallet_settings`.
-   `auto_top_up` (object): Auto top up: charges your default card when an order needs more than your balance.
    -   `enabled` (boolean): When on, your default card is charged `amount` (plus any VAT) when an order needs more than your balance.
    -   `amount` (object): How much each automatic top-up adds.
        -   `amount` (integer): Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.
        -   `currency` (string): Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.

### Responses

-   `200`: Your settings.
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl "https://public-api.dapi.digital/v2/wallet/settings" \
  -H "x-api-key: $DAPI_API_KEY"
```

**Response 200**

```json
{
  "object": "wallet_settings",
  "auto_top_up": {
    "enabled": false,
    "amount": {
      "amount": 5000,
      "currency": "usd"
    }
  }
}
```

## Update wallet settings

PATCH `/v2/wallet/settings`

- **Credentials**: A signed-in dashboard session only; API keys are refused.

- **For**: Integrators

- **Operation id**: `update_wallet_settings`

Turns auto top up on or off and sets its amount. It charges your default card when an order needs more than your balance. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).

### Request body

-   `auto_top_up` (object, required): Auto top up: charges your default card when an order needs more than your balance.
    -   `enabled` (boolean, required): True turns auto top up on, false turns it off.
    -   `amount` (object): Omit to keep the current amount.
        -   `amount` (integer, required): Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.
        -   `currency` (string, required): Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.

### Returns 200 OK

Your settings.

Response attributes (WalletSettings):

-   `object` (string): Names the type of this object. Always `wallet_settings`.
-   `auto_top_up` (object): Auto top up: charges your default card when an order needs more than your balance.
    -   `enabled` (boolean): When on, your default card is charged `amount` (plus any VAT) when an order needs more than your balance.
    -   `amount` (object): How much each automatic top-up adds.
        -   `amount` (integer): Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.
        -   `currency` (string): Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.

### Responses

-   `200`: Your settings.
-   `400`: The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `422`: The auto top up amount is outside $1.00 to $100,000.00 (`amount_out_of_range`).
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl -X PATCH "https://public-api.dapi.digital/v2/wallet/settings" \
  -H "x-api-key: $DAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "auto_top_up": {
    "enabled": true,
    "amount": {
      "amount": 5000,
      "currency": "usd"
    }
  }
}'
```

**Response 200**

```json
{
  "object": "wallet_settings",
  "auto_top_up": {
    "enabled": false,
    "amount": {
      "amount": 5000,
      "currency": "usd"
    }
  }
}
```

## List withdrawals

GET `/v2/withdrawals`

- **Credentials**: A signed-in dashboard session only; API keys are refused.

- **For**: Integrators

- **Operation id**: `list_withdrawals`

Your withdrawal requests, newest first. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).

### Query parameters

-   `limit` (integer): How many results per page, from 1 to 100.
    
    Range 1 to 100. Defaults to 20.
    
-   `cursor` (string): Where to continue from: the `next_cursor` of the previous page. Omit for the first page.

### Returns 200 OK

A page of requests.

Response attributes (WithdrawalList):

-   `object` (string): Names the type of this object. Always `list`.
-   `data` (array of Withdrawal): The page of results.
    -   `id` (string): Unique identifier of the withdrawal, prefixed with `wdr_`. Treat it as an opaque string: store it and compare it, never parse it.
    -   `object` (string): Names the type of this object. Always `withdrawal`.
    -   `amount` (object): An amount of money. Amounts are integers in the smallest currency unit, so they never need decimal parsing.
        -   `amount` (integer): Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.
        -   `currency` (string): Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.
    -   `status` (WithdrawalStatus):
        
        -   `pending`: waiting for 3DAPI to pay it out. The amount is already off your balance.
        -   `completed`: paid out to your payout details.
        -   `cancelled`: you cancelled it; the amount went back to your balance.
        -   `rejected`: 3DAPI refused it (`note` says why); the amount went back to your balance.
        
    -   `payment_link` (string or null): The payment link you gave, when you gave one.
    -   `invoice_url` (string or null): A short-lived link to the invoice you attached, when you attached one.
    -   `note` (string or null): What 3DAPI wrote when paying or rejecting it.
    -   `processed_at` (timestamp or null): Point in time, ISO 8601 in UTC.
    -   `created_at` (timestamp): Point in time, ISO 8601 in UTC.
-   `has_more` (boolean): True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`.
-   `next_cursor` (string or null): Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.

### Responses

-   `200`: A page of requests.
-   `400`: The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl "https://public-api.dapi.digital/v2/withdrawals" \
  -H "x-api-key: $DAPI_API_KEY"
```

**Response 200**

```json
{
  "object": "list",
  "data": [
    {
      "id": "wdr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
      "object": "withdrawal",
      "amount": {
        "amount": 25000,
        "currency": "usd"
      },
      "status": "pending",
      "payment_link": null,
      "invoice_url": "https://files.dapi.digital/invoices/acme-1021.pdf?sig=abc",
      "note": null,
      "processed_at": null,
      "created_at": "2026-10-21T09:30:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

## Request a withdrawal

POST `/v2/withdrawals`

- **Credentials**: A signed-in dashboard session only; API keys are refused.

- **For**: Integrators

- **Operation id**: `create_withdrawal`

Asks 3DAPI to pay out part of your balance to your payout details. The amount leaves your balance now and is held until the payout is made; cancelling or a rejection puts it back.

Attach an invoice in USD (an upload with purpose `withdrawal_invoice`) or a payment link. One request every 14 days.

Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).

### Request body

-   `amount` (object, required): How much to withdraw. Your whole balance can be withdrawn.
    -   `amount` (integer, required): Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.
    -   `currency` (string, required): Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.
-   `payment_link` (string or null): A payment link for this payout, shown to 3DAPI staff with the request. Send it, `invoice`, or both. Omit or null for none.
-   `invoice` (object or null): Your invoice for the amount. Send it, `payment_link`, or both. Omit or null for none.
    -   `upload` (string, required): An upload id with purpose `withdrawal_invoice`: a PDF invoice in USD.

### Returns 201 Created

The pending request.

Response attributes (Withdrawal):

-   `id` (string): Unique identifier of the withdrawal, prefixed with `wdr_`. Treat it as an opaque string: store it and compare it, never parse it.
-   `object` (string): Names the type of this object. Always `withdrawal`.
-   `amount` (object): An amount of money. Amounts are integers in the smallest currency unit, so they never need decimal parsing.
    -   `amount` (integer): Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.
    -   `currency` (string): Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.
-   `status` (WithdrawalStatus): Same shape as `WithdrawalStatus` above.
-   `payment_link` (string or null): The payment link you gave, when you gave one.
-   `invoice_url` (string or null): A short-lived link to the invoice you attached, when you attached one.
-   `note` (string or null): What 3DAPI wrote when paying or rejecting it.
-   `processed_at` (timestamp or null): Point in time, ISO 8601 in UTC.
-   `created_at` (timestamp): Point in time, ISO 8601 in UTC.

### Responses

-   `201`: The pending request.
-   `400`: Neither an invoice nor a payment link (`invoice_or_payment_link_required`), or the request is invalid.
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `409`: A request is pending (`withdrawal_pending`), or the cooldown has not ended (`withdrawal_cooldown`, `details.next_available_at`).
-   `422`: The amount is more than your balance (`insufficient_balance`), or the upload is unknown, expired or of another purpose.
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user), plus 60 per minute per API key (or signed-in user) for quote, upload, order and payment operations. Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl -X POST "https://public-api.dapi.digital/v2/withdrawals" \
  -H "x-api-key: $DAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "amount": {
    "amount": 25000,
    "currency": "usd"
  },
  "invoice": {
    "upload": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
  }
}'
```

**Response 201**

```json
{
  "id": "wdr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
  "object": "withdrawal",
  "amount": {
    "amount": 25000,
    "currency": "usd"
  },
  "status": "pending",
  "payment_link": null,
  "invoice_url": "https://files.dapi.digital/invoices/acme-1021.pdf?sig=abc",
  "note": null,
  "processed_at": null,
  "created_at": "2026-10-21T09:30:00Z"
}
```

## Check whether you can withdraw

GET `/v2/withdrawals/eligibility`

- **Credentials**: A signed-in dashboard session only; API keys are refused.

- **For**: Integrators

- **Operation id**: `get_withdrawal_eligibility`

One withdrawal every 14 days, counted from your last paid-out request; a pending request holds the slot. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).

### Returns 200 OK

Whether you can withdraw now.

Response attributes (WithdrawalEligibility):

-   `object` (string): Names the type of this object. Always `withdrawal_eligibility`.
-   `can_withdraw_now` (boolean): True when no request is pending and the cooldown has ended. It does not check your balance.
-   `cooldown_days` (integer): Days between paid-out requests: 14 for integrators.
-   `has_pending` (boolean): A pending request holds the slot until it is processed or cancelled.
-   `next_available_at` (timestamp or null): When the cooldown after your last paid-out request ends; null when you are not in one.

### Responses

-   `200`: Whether you can withdraw now.
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl "https://public-api.dapi.digital/v2/withdrawals/eligibility" \
  -H "x-api-key: $DAPI_API_KEY"
```

**Response 200**

```json
{
  "object": "withdrawal_eligibility",
  "can_withdraw_now": true,
  "cooldown_days": 14,
  "has_pending": false,
  "next_available_at": null
}
```

## Cancel a withdrawal

POST `/v2/withdrawals/{id}/cancel`

- **Credentials**: A signed-in dashboard session only; API keys are refused.

- **For**: Integrators

- **Operation id**: `cancel_withdrawal`

Cancels a pending request; its amount returns to your balance. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).

### Path parameters

-   `id` (string, required): Unique identifier of the withdrawal, prefixed with `wdr_`. Treat it as an opaque string: store it and compare it, never parse it.

### Returns 200 OK

The cancelled request.

Response attributes (Withdrawal):

-   `id` (string): Unique identifier of the withdrawal, prefixed with `wdr_`. Treat it as an opaque string: store it and compare it, never parse it.
-   `object` (string): Names the type of this object. Always `withdrawal`.
-   `amount` (object): An amount of money. Amounts are integers in the smallest currency unit, so they never need decimal parsing.
    -   `amount` (integer): Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.
    -   `currency` (string): Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.
-   `status` (WithdrawalStatus): Same shape as `WithdrawalStatus` above.
-   `payment_link` (string or null): The payment link you gave, when you gave one.
-   `invoice_url` (string or null): A short-lived link to the invoice you attached, when you attached one.
-   `note` (string or null): What 3DAPI wrote when paying or rejecting it.
-   `processed_at` (timestamp or null): Point in time, ISO 8601 in UTC.
-   `created_at` (timestamp): Point in time, ISO 8601 in UTC.

### Responses

-   `200`: The cancelled request.
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `404`: No such request (`withdrawal_not_found`).
-   `409`: It is no longer pending (`withdrawal_not_pending`).
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl -X POST "https://public-api.dapi.digital/v2/withdrawals/wdr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu/cancel" \
  -H "x-api-key: $DAPI_API_KEY"
```

**Response 200**

```json
{
  "id": "wdr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
  "object": "withdrawal",
  "amount": {
    "amount": 25000,
    "currency": "usd"
  },
  "status": "cancelled",
  "payment_link": null,
  "invoice_url": "https://files.dapi.digital/invoices/acme-1021.pdf?sig=abc",
  "note": null,
  "processed_at": "2026-10-21T10:00:00Z",
  "created_at": "2026-10-21T09:30:00Z"
}
```

## Get payout details

GET `/v2/payout-details`

- **Credentials**: A signed-in dashboard session only; API keys are refused.

- **For**: Integrators

- **Operation id**: `get_payout_details`

The bank account withdrawals are paid to. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).

### Returns 200 OK

Your payout details.

Response attributes (PayoutDetails):

-   `object` (string): Names the type of this object. Always `payout_details`.
-   `bank_name` (string): Name of the bank holding the account.
-   `account_holder_name` (string): Name the account is held in.
-   `account_number_last4` (string): The last 4 characters of the account number or IBAN. The full number is never returned; send it again in full to change it.
-   `routing_number` (string): Routing number, sort code or SWIFT/BIC.
-   `updated_at` (timestamp or null): Point in time, ISO 8601 in UTC.

### Responses

-   `200`: Your payout details.
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `404`: You have not added payout details yet (`payout_details_not_found`).
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl "https://public-api.dapi.digital/v2/payout-details" \
  -H "x-api-key: $DAPI_API_KEY"
```

**Response 200**

```json
{
  "object": "payout_details",
  "bank_name": "Deutsche Bank",
  "account_holder_name": "Acme Prints Ltd",
  "account_number_last4": "3000",
  "routing_number": "COBADEFFXXX",
  "updated_at": "2026-10-21T09:30:00Z"
}
```

## Save payout details

PUT `/v2/payout-details`

- **Credentials**: A signed-in dashboard session only; API keys are refused.

- **For**: Integrators

- **Operation id**: `save_payout_details`

Replaces the bank account withdrawals are paid to. Bank transfer is the only payout method. Integrators only, from a signed-in dashboard session: an API key cannot manage money (`credential_not_allowed`).

### Request body

-   `bank_name` (string, required): Name of the bank holding the account.
-   `account_holder_name` (string, required): Name the account is held in, as the bank has it.
-   `account_number` (string, required): The full account number or IBAN.
-   `routing_number` (string, required): Routing number, sort code or SWIFT/BIC of the bank.

### Returns 200 OK

The saved details.

Response attributes (PayoutDetails):

-   `object` (string): Names the type of this object. Always `payout_details`.
-   `bank_name` (string): Name of the bank holding the account.
-   `account_holder_name` (string): Name the account is held in.
-   `account_number_last4` (string): The last 4 characters of the account number or IBAN. The full number is never returned; send it again in full to change it.
-   `routing_number` (string): Routing number, sort code or SWIFT/BIC.
-   `updated_at` (timestamp or null): Point in time, ISO 8601 in UTC.

### Responses

-   `200`: The saved details.
-   `400`: The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl -X PUT "https://public-api.dapi.digital/v2/payout-details" \
  -H "x-api-key: $DAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "bank_name": "Deutsche Bank",
  "account_holder_name": "Acme Prints Ltd",
  "account_number": "DE89370400440532013000",
  "routing_number": "COBADEFFXXX"
}'
```

**Response 200**

```json
{
  "object": "payout_details",
  "bank_name": "Deutsche Bank",
  "account_holder_name": "Acme Prints Ltd",
  "account_number_last4": "3000",
  "routing_number": "COBADEFFXXX",
  "updated_at": "2026-10-21T09:30:00Z"
}
```

## The transaction object

One movement of your money for orders, stock or the wallet.

### Attributes

-   `id` (string): Unique identifier of the transaction, prefixed with `txn_`. Treat it as an opaque string: store it and compare it, never parse it.
-   `object` (string): Names the type of this object. Always `transaction`.
-   `direction` (TransactionDirection): `credit` added money for you, `debit` took it. Amounts are always positive; this gives the sign.
-   `category` (TransactionCategory):
    
    What the money was for:
    
    -   `order_payment`: you paid for a manual, imported or API order, from the wallet or by card.
    -   `stock_purchase`: you bought stock batches, including automatic reorders.
    -   `top_up`: you added money to the wallet by card, manually, automatically or to cover a shortfall.
    -   `earnings`: your earnings from a completed storefront order or, for integrators, a completed checkout order. A `debit` when the order's sales did not cover manufacturing, the platform fee and the card fee: the difference is charged to your wallet at completion.
    -   `refund`: an order refund returned to your wallet.
    -   `withdrawal`: money you withdrew from the wallet.
    -   `adjustment`: a credit or debit that is not one of the above. `adjustment_reason` says which kind, and `description` explains it in words.
    
-   `adjustment_reason` (string or null): Why the adjustment happened, when `category` is `adjustment`; null for every other category.
    
    Possible values: `support_credit`, `support_debit`, `shipping_discount`, `warehousing_charge`, `replacement_order_charge`.
    
-   `paid_with` (TransactionPaidWith): `wallet` when the money moved through your balance, `card` when a card was charged directly (no-profit orders paid at checkout). Filter on `wallet` to reconcile with the balance.
-   `status` (TransactionStatus): `pending` only for withdrawals requested and not paid out yet; everything else is `completed`.
-   `amount` (object): Amount moved, VAT included. Always positive. On a `top_up` this is what your card was charged; your balance grows by the net amount (`amount` minus `vat.amount`), so top-up amounts do not add up to your balance.
    -   `amount` (integer): Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.
    -   `currency` (string): Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.
-   `vat` (object or null): VAT included in `amount`, when any was charged.
    -   `amount` (object): An amount of money. Amounts are integers in the smallest currency unit, so they never need decimal parsing.
        -   `amount` (integer): Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.
        -   `currency` (string): Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.
    -   `rate` (number): VAT rate as a fraction, for example 0.21.
        
        Range 0 to 1.
        
-   `description` (string or null): What happened, ready to show to a person.
-   `orders` (array of OrderReference): The orders this transaction paid for or came from.
    -   `id` (string): Unique identifier of the order, prefixed with `ord_`. Treat it as an opaque string: store it and compare it, never parse it.
    -   `object` (string): Names the type of this object. Always `order`.
    -   `name` (string): Display name of the order.
-   `invoice_url` (string or null): Invoice or receipt for card charges and top-ups, when one exists.
-   `created_at` (timestamp): Point in time, ISO 8601 in UTC.

**The transaction object**

```json
null
```

## List transactions

GET `/v2/transactions`

- **Credentials**: An API key with the `wallet:read` scope (full access includes every scope). A signed-in dashboard session works too.

- **For**: Designers and Integrators

- **Operation id**: `list_transactions`

Lists every movement of your money for orders, stock and the wallet, newest first: wallet payments and top-ups, card-paid orders, earnings, refunds and withdrawals. Plan subscription charges are not included. For designers and integrators.

### Query parameters

-   `direction` (string): Only credits or only debits.
    
    Possible values: `credit`, `debit`.
    
-   `category` (array of TransactionCategory): Only these categories. Repeat for several, for example `category=order_payment&category=stock_purchase`. Same shape as `TransactionCategory` above.
-   `paid_with` (string): Only wallet movements or only card charges.
    
    Possible values: `wallet`, `card`.
    
-   `status` (string): Only pending or only completed transactions.
    
    Possible values: `pending`, `completed`.
    
-   `created_after` (timestamp): Only transactions after this time. When neither created\_after nor created\_before is given, defaults to 30 days before the request. Any explicit range is honoured as given, with no maximum length.
-   `created_before` (timestamp): Only transactions before this time. Giving it on its own turns off the 30 day default, so there is no lower bound.
-   `amount_min` (integer or null): Only amounts of at least this many cents.
    
    Range at least 0.
    
-   `amount_max` (integer or null): Only amounts of at most this many cents.
    
    Range at least 0.
    
-   `order` (string): Only transactions for this order: an `ord_` id or an order number.
-   `search` (string): Matches the description and order numbers.
-   `sort` (string): Order of results. A leading `-` means descending.
    
    Possible values: `-created_at`, `created_at`, `-amount`, `amount`.
    
    Defaults to "-created\_at".
    
-   `limit` (integer): How many results per page, from 1 to 100.
    
    Range 1 to 100. Defaults to 20.
    
-   `cursor` (string): Where to continue from: the `next_cursor` of the previous page. Omit for the first page.

### Returns 200 OK

A page of transactions.

Response attributes (TransactionList):

-   `object` (string): Names the type of this object. Always `list`.
-   `data` (array of Transaction): The page of results.
    
    Described in [the transaction object](https://public-api.dapi.digital/docs/reference/wallet.md#transaction_object).
    
-   `has_more` (boolean): True when more results exist after this page. Fetch them by passing `next_cursor` as `cursor`.
-   `next_cursor` (string or null): Opaque cursor for the next page, or null on the last page. Pass it back unchanged as `cursor`.

### Responses

-   `200`: A page of transactions.
-   `400`: The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl "https://public-api.dapi.digital/v2/transactions" \
  -H "x-api-key: $DAPI_API_KEY"
```

**Response 200**

```json
{
  "object": "list",
  "data": [
    {
      "id": "txn_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
      "object": "transaction",
      "direction": "debit",
      "category": "order_payment",
      "adjustment_reason": null,
      "paid_with": "wallet",
      "status": "completed",
      "amount": {
        "amount": 5299,
        "currency": "usd"
      },
      "vat": null,
      "description": "ShipStation order SSG2M9S9",
      "orders": [
        {
          "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
          "object": "order",
          "name": "SSG2M9S9"
        }
      ],
      "invoice_url": null,
      "created_at": "2026-10-12T14:03:55Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

## Summarize transactions

GET `/v2/transactions/summary`

- **Credentials**: An API key with the `wallet:read` scope (full access includes every scope). A signed-in dashboard session works too.

- **For**: Designers and Integrators

- **Operation id**: `get_transaction_summary`

Totals your completed transactions over a period, per category and direction. Use it for monthly reporting instead of paging through every transaction. For designers and integrators.

### Query parameters

-   `created_after` (timestamp): Only transactions after this time. When neither created\_after nor created\_before is given, defaults to 30 days before the request. Any explicit range is honoured as given, with no maximum length.
-   `created_before` (timestamp): Only transactions before this time. Giving it on its own turns off the 30 day default, so there is no lower bound.
-   `paid_with` (string): Only wallet movements or only card charges.
    
    Possible values: `wallet`, `card`.
    

### Returns 200 OK

The totals.

Response attributes (TransactionSummary):

-   `object` (string): Names the type of this object. Always `transaction_summary`.
-   `created_after` (timestamp or null): Start of the period applied: as requested, or 30 days before the request when neither created\_after nor created\_before was given. Null for no lower bound.
-   `created_before` (timestamp or null): End of the period, as requested. Null for no upper bound.
-   `credits` (object): Total credited in the period.
    -   `amount` (integer): Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.
    -   `currency` (string): Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.
-   `debits` (object): Total debited in the period.
    -   `amount` (integer): Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.
    -   `currency` (string): Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.
-   `net` (object): `credits` minus `debits`. Negative when you spent more than came in.
    -   `amount` (integer): Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.
    -   `currency` (string): Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.
-   `by_category` (array of object): Totals per category and direction. Categories with no transactions are left out.
    -   `category` (TransactionCategory): Same shape as `TransactionCategory` above.
    -   `direction` (TransactionDirection): Same shape as `TransactionDirection` above.
    -   `count` (integer): How many completed transactions of this category and direction the period holds.
        
        Range at least 0.
        
    -   `total` (object): An amount of money. Amounts are integers in the smallest currency unit, so they never need decimal parsing.
        -   `amount` (integer): Amount in the smallest currency unit (cents for usd). 5000 means $50.00. Never a decimal.
        -   `currency` (string): Three-letter ISO 4217 currency code, lowercase. Every amount is `usd` today.

### Responses

-   `200`: The totals.
-   `400`: The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl "https://public-api.dapi.digital/v2/transactions/summary" \
  -H "x-api-key: $DAPI_API_KEY"
```

**Response 200**

```json
{
  "object": "transaction_summary",
  "created_after": "2026-09-01T00:00:00Z",
  "created_before": "2026-10-01T00:00:00Z",
  "credits": {
    "amount": 50000,
    "currency": "usd"
  },
  "debits": {
    "amount": 41230,
    "currency": "usd"
  },
  "net": {
    "amount": 8770,
    "currency": "usd"
  },
  "by_category": [
    {
      "category": "top_up",
      "direction": "credit",
      "count": 2,
      "total": {
        "amount": 50000,
        "currency": "usd"
      }
    },
    {
      "category": "order_payment",
      "direction": "debit",
      "count": 31,
      "total": {
        "amount": 29230,
        "currency": "usd"
      }
    },
    {
      "category": "stock_purchase",
      "direction": "debit",
      "count": 2,
      "total": {
        "amount": 12000,
        "currency": "usd"
      }
    }
  ]
}
```

---

3DAPI API Docs. This page on the web: https://public-api.dapi.digital/docs/reference/wallet. Index of every page: https://public-api.dapi.digital/docs/llms.txt.
