---
title: "Customers"
description: "10 operations, with parameters and examples."
url: https://docs.fluveo.com/api/customers/
---

> Index of every page: https://docs.fluveo.com/llms.txt. Integrating Fluveo? Read https://docs.fluveo.com/agents/integrate.md first.

> Too long? This page with code in one language only: https://docs.fluveo.com/api/customers.curl.md (cURL), https://docs.fluveo.com/api/customers.node.md (Node.js), https://docs.fluveo.com/api/customers.python.md (Python), https://docs.fluveo.com/api/customers.php.md (PHP).

# Customers

10 operations, with parameters and examples.

A Customer is a buyer the store keeps on file, with contact details, saved payment methods and tax ids. Find customers by their fields with the search route below. Deleting a customer sends only customer.deleted: its saved payment methods keep their customer.

Send every request to `https://api.fluveo.com/v1` with your secret key as a Bearer token. This page lists only what Fluveo serves; [the OpenAPI file](https://docs.fluveo.com/openapi.json) holds the same operations.

An operation or a parameter marked **Preview** works, but it is not locked yet and may change: [what Preview means](https://docs.fluveo.com/api.md#preview).

> **For AI agents:**
>
> Use plain HTTP; install no payments SDK. Send `Authorization: Bearer sk_test_...` and a fresh `Idempotency-Key` on every POST; reuse the same key only when you retry that same POST. Send only the documented parameters. An operation or a parameter marked Preview may change: prefer the ones without the mark.

## Creates a customer

`POST /v1/customers`

Permission: [`rak_customer_write`](https://docs.fluveo.com/permissions.md#customer).

**Headers**

- `Idempotency-Key` (string, optional): Send a unique key, 1 to 255 characters, on every POST. A retry with the same key and the same body returns the first answer instead of acting twice (with Idempotent-Replayed: true); the same key with another body is refused. Keys are kept 7 days, except on POST /v1/checkout/sessions, POST /v1/checkout/sessions/{session} and POST /v1/webhook_endpoints, where a key holds 24 hours; webhook endpoint keys are also capped at the newest 32 per store and mode. Do not rely on a replay after that window. GET and DELETE ignore it.
- `Fluveo-Version` (enum, optional): Optional. The only version Fluveo serves is 2026-05-27.dahlia; any other value is refused with a 400. Every answer names that version in its own Fluveo-Version header, except the refusals Fluveo gives before it reads the request (no key, an unknown key, an unknown URL, a body that is not a form) and a file's bytes, which name no version and no Request-Id.

  One of: `2026-05-27.dahlia`.

**Body parameters**

- `address` (object, optional, preview): The customer's address: line1, line2, city, state, postal_code, country.
  - `city` (string, optional)
  - `country` (string, optional)
  - `line1` (string, optional)
  - `line2` (string, optional)
  - `postal_code` (string, optional)
  - `state` (string, optional)
- `description` (string, optional, preview): Free text for your own records.
- `email` (string, optional): The customer's email address.
- `metadata` (object, optional): Up to 50 key-value pairs of your own, sent as metadata[key]=value: keys of 1 to 40 characters, values up to 500. Keys starting with fluveo_ are reserved. On an update, an empty value removes a key.
- `name` (string, optional): The name of the person or the company you bill.
- `phone` (string, optional, preview): The customer's phone number.
- `preferred_locales` (array of string, optional, preview): The customer's languages, most preferred first, as IETF language tags such as en-US or pt-BR, sent as preferred_locales[]=en-US. Each tag is stored in its usual letter case (en-us becomes en-US); a tag that is not one is refused. Send preferred_locales= to empty the list.
- `shipping` (object, optional, preview): Shipping name, address and phone for the order.
  - `address` (object, optional)
    - `city` (string, optional)
    - `country` (string, optional)
    - `line1` (string, optional)
    - `line2` (string, optional)
    - `postal_code` (string, optional)
    - `state` (string, optional)
  - `name` (string, optional)
  - `phone` (string, optional)
- `tax_exempt` (enum, optional, preview): The customer's tax status: none, exempt or reverse. An empty value is ignored, so a new customer is none and an update keeps the status it had. It is kept and shown only.

  One of: `none`, `exempt`, `reverse`.
- `tax_id_data` (array of object, optional, preview): The Customer's tax ids, created with it as its tax_ids list: tax_id_data[0][type] and tax_id_data[0][value], then [1] and so on. Pix and Boleto read the payer's CPF or CNPJ from here.
  - `type` (enum, required): The kind of tax id: br_cpf for a CPF, br_cnpj for a CNPJ.

    One of: `br_cpf`, `br_cnpj`.
  - `value` (string, required): The CPF (11 digits) or CNPJ (14 digits); dots, dashes and slashes are ignored, and a number with wrong check digits is refused.

Use only the documented body parameters. Send a new `Idempotency-Key` with each new request, and the same key again only to retry that request.

Returns a [`customer`](https://docs.fluveo.com/api/customers.md#object-customer) object.

**Request**

cURL:

```bash
curl https://api.fluveo.com/v1/customers \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  --data-urlencode "email=ada@example.com" \
  --data-urlencode "name=Ada Lovelace"
```

Node.js:

```javascript
const response = await fetch("https://api.fluveo.com/v1/customers", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk_test_...",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: new URLSearchParams([["email","ada@example.com"],["name","Ada Lovelace"]]),
});
console.log(await response.json());
```

Python:

```python
import uuid

import requests

response = requests.post(
    "https://api.fluveo.com/v1/customers",
    headers={"Authorization": "Bearer sk_test_...", "Idempotency-Key": str(uuid.uuid4())},
    data=[("email", "ada@example.com"), ("name", "Ada Lovelace")],
)
print(response.json())
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/customers");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_test_...",
        "Idempotency-Key: " . bin2hex(random_bytes(16)),
    ],
    CURLOPT_POSTFIELDS => "email=ada%40example.com&name=Ada%20Lovelace",
]);
echo curl_exec($ch);
```

*Response (abridged example)*

```json
{
  "created": 1767225600,
  "email": "ada@example.com",
  "id": "cus_123",
  "livemode": false,
  "metadata": {},
  "name": "Ada Lovelace",
  "object": "customer"
}
```

## Lists customer objects, newest first

`GET /v1/customers`

Permission: [`rak_customer_read`](https://docs.fluveo.com/permissions.md#customer).

**Query parameters**

- `created` (object, optional, preview): Filter on the creation time, a Unix timestamp: created=<t> for an exact match, or created[gt], created[gte], created[lt], created[lte].
- `email` (string, optional): Only the customers with exactly this email, up to 512 characters.
- `ending_before` (string, optional, preview): Cursor: the id of the first object of the current page, to get the previous page. Do not send it with starting_after.
- `limit` (integer, optional): How many objects to return, from 1 to 100. Default 10.
- `starting_after` (string, optional, preview): Cursor: the id of the last object of the previous page, to get the next page. Do not send it with ending_before.

**Headers**

- `Fluveo-Version` (enum, optional): Optional. The only version Fluveo serves is 2026-05-27.dahlia; any other value is refused with a 400. Every answer names that version in its own Fluveo-Version header, except the refusals Fluveo gives before it reads the request (no key, an unknown key, an unknown URL, a body that is not a form) and a file's bytes, which name no version and no Request-Id.

  One of: `2026-05-27.dahlia`.

Returns a list of [`customer`](https://docs.fluveo.com/api/customers.md#object-customer) objects.

**Request**

cURL:

```bash
curl -G https://api.fluveo.com/v1/customers \
  -H "Authorization: Bearer sk_test_..." \
  --data-urlencode "email=ada@example.com"
```

Node.js:

```javascript
const response = await fetch("https://api.fluveo.com/v1/customers" + "?" + new URLSearchParams([["email","ada@example.com"]]), {
  method: "GET",
  headers: {
    "Authorization": "Bearer sk_test_...",
  },
});
console.log(await response.json());
```

Python:

```python
import requests

response = requests.get(
    "https://api.fluveo.com/v1/customers",
    headers={"Authorization": "Bearer sk_test_..."},
    params=[("email", "ada@example.com")],
)
print(response.json())
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/customers?email=ada%40example.com");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "GET",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_test_...",
    ],
]);
echo curl_exec($ch);
```

*Response (abridged example)*

```json
{
  "data": [
    {
      "created": 1767225600,
      "email": "ada@example.com",
      "id": "cus_123",
      "livemode": false,
      "metadata": {},
      "name": "Ada Lovelace",
      "object": "customer"
    }
  ],
  "has_more": false,
  "object": "list",
  "url": "/v1/customers"
}
```

## Updates a customer

`POST /v1/customers/{customer}`

Permission: [`rak_customer_write`](https://docs.fluveo.com/permissions.md#customer).

**Path parameters**

- `customer` (string, required): The Customer's id.

**Headers**

- `Idempotency-Key` (string, optional): Send a unique key, 1 to 255 characters, on every POST. A retry with the same key and the same body returns the first answer instead of acting twice (with Idempotent-Replayed: true); the same key with another body is refused. Keys are kept 7 days, except on POST /v1/checkout/sessions, POST /v1/checkout/sessions/{session} and POST /v1/webhook_endpoints, where a key holds 24 hours; webhook endpoint keys are also capped at the newest 32 per store and mode. Do not rely on a replay after that window. GET and DELETE ignore it.
- `Fluveo-Version` (enum, optional): Optional. The only version Fluveo serves is 2026-05-27.dahlia; any other value is refused with a 400. Every answer names that version in its own Fluveo-Version header, except the refusals Fluveo gives before it reads the request (no key, an unknown key, an unknown URL, a body that is not a form) and a file's bytes, which name no version and no Request-Id.

  One of: `2026-05-27.dahlia`.

**Body parameters**

- `address` (object, optional, preview): The customer's address: line1, line2, city, state, postal_code, country.
  - `city` (string, optional)
  - `country` (string, optional)
  - `line1` (string, optional)
  - `line2` (string, optional)
  - `postal_code` (string, optional)
  - `state` (string, optional)
- `description` (string, optional, preview): Free text for your own records.
- `email` (string, optional, preview): The customer's email address.
- `metadata` (object, optional): Up to 50 key-value pairs of your own, sent as metadata[key]=value: keys of 1 to 40 characters, values up to 500. Keys starting with fluveo_ are reserved. On an update, an empty value removes a key.
- `name` (string, optional, preview): The name of the person or the company you bill.
- `phone` (string, optional, preview): The customer's phone number.
- `preferred_locales` (array of string, optional, preview): The customer's languages, most preferred first, as IETF language tags such as en-US or pt-BR, sent as preferred_locales[]=en-US. Each tag is stored in its usual letter case (en-us becomes en-US); a tag that is not one is refused. Send preferred_locales= to empty the list.
- `shipping` (object, optional, preview): Shipping name, address and phone for the order.
  - `address` (object, optional)
    - `city` (string, optional)
    - `country` (string, optional)
    - `line1` (string, optional)
    - `line2` (string, optional)
    - `postal_code` (string, optional)
    - `state` (string, optional)
  - `name` (string, optional)
  - `phone` (string, optional)
- `tax_exempt` (enum, optional, preview): The customer's tax status: none, exempt or reverse. An empty value is ignored, so a new customer is none and an update keeps the status it had. It is kept and shown only.

  One of: `none`, `exempt`, `reverse`.

Use only the documented body parameters. Send a new `Idempotency-Key` with each new request, and the same key again only to retry that request.

Returns a [`customer`](https://docs.fluveo.com/api/customers.md#object-customer) object.

**Request**

cURL:

```bash
curl https://api.fluveo.com/v1/customers/cus_123 \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  --data-urlencode "phone=+15555550123"
```

Node.js:

```javascript
const response = await fetch("https://api.fluveo.com/v1/customers/cus_123", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk_test_...",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: new URLSearchParams([["phone","+15555550123"]]),
});
console.log(await response.json());
```

Python:

```python
import uuid

import requests

response = requests.post(
    "https://api.fluveo.com/v1/customers/cus_123",
    headers={"Authorization": "Bearer sk_test_...", "Idempotency-Key": str(uuid.uuid4())},
    data=[("phone", "+15555550123")],
)
print(response.json())
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/customers/cus_123");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_test_...",
        "Idempotency-Key: " . bin2hex(random_bytes(16)),
    ],
    CURLOPT_POSTFIELDS => "phone=%2B15555550123",
]);
echo curl_exec($ch);
```

*Response (abridged example)*

```json
{
  "created": 1767225600,
  "email": "ada@example.com",
  "id": "cus_123",
  "livemode": false,
  "metadata": {},
  "name": "Ada Lovelace",
  "object": "customer"
}
```

## Retrieves a customer (Preview)

`GET /v1/customers/{customer}`

Permission: [`rak_customer_read`](https://docs.fluveo.com/permissions.md#customer).

**Preview:** this operation works, but it is not locked yet; it may change.

**Path parameters**

- `customer` (string, required): The Customer's id.

**Headers**

- `Fluveo-Version` (enum, optional): Optional. The only version Fluveo serves is 2026-05-27.dahlia; any other value is refused with a 400. Every answer names that version in its own Fluveo-Version header, except the refusals Fluveo gives before it reads the request (no key, an unknown key, an unknown URL, a body that is not a form) and a file's bytes, which name no version and no Request-Id.

  One of: `2026-05-27.dahlia`.

Returns a [`customer`](https://docs.fluveo.com/api/customers.md#object-customer) object.

**Request**

cURL:

```bash
curl https://api.fluveo.com/v1/customers/cus_123 \
  -H "Authorization: Bearer sk_test_..."
```

Node.js:

```javascript
const response = await fetch("https://api.fluveo.com/v1/customers/cus_123", {
  method: "GET",
  headers: {
    "Authorization": "Bearer sk_test_...",
  },
});
console.log(await response.json());
```

Python:

```python
import requests

response = requests.get(
    "https://api.fluveo.com/v1/customers/cus_123",
    headers={"Authorization": "Bearer sk_test_..."},
)
print(response.json())
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/customers/cus_123");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "GET",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_test_...",
    ],
]);
echo curl_exec($ch);
```

*Response (abridged example)*

```json
{
  "created": 1767225600,
  "email": "ada@example.com",
  "id": "cus_123",
  "livemode": false,
  "metadata": {},
  "name": "Ada Lovelace",
  "object": "customer"
}
```

## Deletes a deleted customer

`DELETE /v1/customers/{customer}`

Permission: [`rak_customer_write`](https://docs.fluveo.com/permissions.md#customer).

**Path parameters**

- `customer` (string, required): The Customer's id.

**Headers**

- `Fluveo-Version` (enum, optional): Optional. The only version Fluveo serves is 2026-05-27.dahlia; any other value is refused with a 400. Every answer names that version in its own Fluveo-Version header, except the refusals Fluveo gives before it reads the request (no key, an unknown key, an unknown URL, a body that is not a form) and a file's bytes, which name no version and no Request-Id.

  One of: `2026-05-27.dahlia`.

**Request**

cURL:

```bash
curl -X DELETE https://api.fluveo.com/v1/customers/cus_123 \
  -H "Authorization: Bearer sk_test_..."
```

Node.js:

```javascript
const response = await fetch("https://api.fluveo.com/v1/customers/cus_123", {
  method: "DELETE",
  headers: {
    "Authorization": "Bearer sk_test_...",
  },
});
console.log(await response.json());
```

Python:

```python
import requests

response = requests.delete(
    "https://api.fluveo.com/v1/customers/cus_123",
    headers={"Authorization": "Bearer sk_test_..."},
)
print(response.json())
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/customers/cus_123");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "DELETE",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_test_...",
    ],
]);
echo curl_exec($ch);
```

*Response (abridged example)*

```json
{
  "deleted": true,
  "id": "cus_123",
  "object": "customer"
}
```

## Searches customer objects with a query, newest first (Preview)

`GET /v1/customers/search`

Permission: [`rak_customer_read`](https://docs.fluveo.com/permissions.md#customer).

**Preview:** this operation works, but it is not locked yet; it may change.

A query is one or more clauses, each a field, an operator and a quoted value, such as email:"ada@example.com" or name~"ada". The customer fields are email, name, phone, created and metadata["key"]. The operator : matches a run of whole words of email, name or phone, in order and in any case, so email:"ada@example.com" finds that address only. The operator ~ finds a part of email, name or phone of at least three characters; a shorter part matches nothing. created takes a whole number of seconds with =, :, >, >=, < or <=, such as created>1700000000. A - before a clause negates it. field:null matches a field that is absent or empty. Join clauses with AND or OR (a space means AND), never both in one query and never with parentheses, up to 10 clauses. When has_more is true, send next_page as page with the same query for the next page. Add expand[]=total_count to get total_count, the number of matches. A customer is found as soon as its write returns, and a deleted one never.

**Query parameters**

- `limit` (integer, optional): How many objects to return, from 1 to 100. Default 10.
- `page` (string, optional): The next_page of the previous answer, to get the page after it. Send the same query with it.
- `query` (string, required): The search query, in the language the description above gives.

**Headers**

- `Fluveo-Version` (enum, optional): Optional. The only version Fluveo serves is 2026-05-27.dahlia; any other value is refused with a 400. Every answer names that version in its own Fluveo-Version header, except the refusals Fluveo gives before it reads the request (no key, an unknown key, an unknown URL, a body that is not a form) and a file's bytes, which name no version and no Request-Id.

  One of: `2026-05-27.dahlia`.

Returns a list of [`customer`](https://docs.fluveo.com/api/customers.md#object-customer) objects.

**Request**

cURL:

```bash
curl -G https://api.fluveo.com/v1/customers/search \
  -H "Authorization: Bearer sk_test_..." \
  --data-urlencode "query=email:\"ada@example.com\""
```

Node.js:

```javascript
const response = await fetch("https://api.fluveo.com/v1/customers/search" + "?" + new URLSearchParams([["query","email:\"ada@example.com\""]]), {
  method: "GET",
  headers: {
    "Authorization": "Bearer sk_test_...",
  },
});
console.log(await response.json());
```

Python:

```python
import requests

response = requests.get(
    "https://api.fluveo.com/v1/customers/search",
    headers={"Authorization": "Bearer sk_test_..."},
    params=[("query", "email:\"ada@example.com\"")],
)
print(response.json())
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/customers/search?query=email%3A%22ada%40example.com%22");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "GET",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_test_...",
    ],
]);
echo curl_exec($ch);
```

*Response (abridged example)*

```json
{
  "data": [
    {
      "created": 1767225600,
      "email": "ada@example.com",
      "id": "cus_123",
      "livemode": false,
      "metadata": {},
      "name": "Ada Lovelace",
      "object": "customer"
    }
  ],
  "has_more": false,
  "next_page": null,
  "object": "search_result",
  "url": "/v1/customers/search"
}
```

## Creates a tax ID (Preview)

`POST /v1/customers/{customer}/tax_ids`

Permission: [`rak_customer_write`](https://docs.fluveo.com/permissions.md#customer).

**Preview:** this operation works, but it is not locked yet; it may change.

**Path parameters**

- `customer` (string, required): The Customer's id.

**Headers**

- `Idempotency-Key` (string, optional): Send a unique key, 1 to 255 characters, on every POST. A retry with the same key and the same body returns the first answer instead of acting twice (with Idempotent-Replayed: true); the same key with another body is refused. Keys are kept 7 days, except on POST /v1/checkout/sessions, POST /v1/checkout/sessions/{session} and POST /v1/webhook_endpoints, where a key holds 24 hours; webhook endpoint keys are also capped at the newest 32 per store and mode. Do not rely on a replay after that window. GET and DELETE ignore it.
- `Fluveo-Version` (enum, optional): Optional. The only version Fluveo serves is 2026-05-27.dahlia; any other value is refused with a 400. Every answer names that version in its own Fluveo-Version header, except the refusals Fluveo gives before it reads the request (no key, an unknown key, an unknown URL, a body that is not a form) and a file's bytes, which name no version and no Request-Id.

  One of: `2026-05-27.dahlia`.

**Body parameters**

- `type` (enum, required): The kind of tax id: br_cpf for a CPF, br_cnpj for a CNPJ.

  One of: `br_cpf`, `br_cnpj`.
- `value` (string, required): The tax id itself, for example a CPF or CNPJ.

Use only the documented body parameters. Send a new `Idempotency-Key` with each new request, and the same key again only to retry that request.

Returns a [`tax_id`](https://docs.fluveo.com/api/customers.md#object-tax_id) object.

**Request**

cURL:

```bash
curl https://api.fluveo.com/v1/customers/cus_123/tax_ids \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  --data-urlencode "type=br_cpf" \
  --data-urlencode "value=123.456.789-09"
```

Node.js:

```javascript
const response = await fetch("https://api.fluveo.com/v1/customers/cus_123/tax_ids", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk_test_...",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: new URLSearchParams([["type","br_cpf"],["value","123.456.789-09"]]),
});
console.log(await response.json());
```

Python:

```python
import uuid

import requests

response = requests.post(
    "https://api.fluveo.com/v1/customers/cus_123/tax_ids",
    headers={"Authorization": "Bearer sk_test_...", "Idempotency-Key": str(uuid.uuid4())},
    data=[("type", "br_cpf"), ("value", "123.456.789-09")],
)
print(response.json())
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/customers/cus_123/tax_ids");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_test_...",
        "Idempotency-Key: " . bin2hex(random_bytes(16)),
    ],
    CURLOPT_POSTFIELDS => "type=br_cpf&value=123.456.789-09",
]);
echo curl_exec($ch);
```

## Lists tax ID objects of the parent object (Preview)

`GET /v1/customers/{customer}/tax_ids`

Permission: [`rak_customer_read`](https://docs.fluveo.com/permissions.md#customer).

**Preview:** this operation works, but it is not locked yet; it may change.

**Path parameters**

- `customer` (string, required): The Customer's id.

**Query parameters**

- `ending_before` (string, optional): Cursor: the id of the first object of the current page, to get the previous page. Do not send it with starting_after.
- `limit` (integer, optional): How many objects to return, from 1 to 100. Default 10.
- `starting_after` (string, optional): Cursor: the id of the last object of the previous page, to get the next page. Do not send it with ending_before.

**Headers**

- `Fluveo-Version` (enum, optional): Optional. The only version Fluveo serves is 2026-05-27.dahlia; any other value is refused with a 400. Every answer names that version in its own Fluveo-Version header, except the refusals Fluveo gives before it reads the request (no key, an unknown key, an unknown URL, a body that is not a form) and a file's bytes, which name no version and no Request-Id.

  One of: `2026-05-27.dahlia`.

Returns a list of [`tax_id`](https://docs.fluveo.com/api/customers.md#object-tax_id) objects.

**Request**

cURL:

```bash
curl https://api.fluveo.com/v1/customers/cus_123/tax_ids \
  -H "Authorization: Bearer sk_test_..."
```

Node.js:

```javascript
const response = await fetch("https://api.fluveo.com/v1/customers/cus_123/tax_ids", {
  method: "GET",
  headers: {
    "Authorization": "Bearer sk_test_...",
  },
});
console.log(await response.json());
```

Python:

```python
import requests

response = requests.get(
    "https://api.fluveo.com/v1/customers/cus_123/tax_ids",
    headers={"Authorization": "Bearer sk_test_..."},
)
print(response.json())
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/customers/cus_123/tax_ids");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "GET",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_test_...",
    ],
]);
echo curl_exec($ch);
```

## Retrieves a tax ID (Preview)

`GET /v1/customers/{customer}/tax_ids/{id}`

Permission: [`rak_customer_read`](https://docs.fluveo.com/permissions.md#customer).

**Preview:** this operation works, but it is not locked yet; it may change.

**Path parameters**

- `customer` (string, required): The Customer's id.
- `id` (string, required): The object's id.

**Headers**

- `Fluveo-Version` (enum, optional): Optional. The only version Fluveo serves is 2026-05-27.dahlia; any other value is refused with a 400. Every answer names that version in its own Fluveo-Version header, except the refusals Fluveo gives before it reads the request (no key, an unknown key, an unknown URL, a body that is not a form) and a file's bytes, which name no version and no Request-Id.

  One of: `2026-05-27.dahlia`.

Returns a [`tax_id`](https://docs.fluveo.com/api/customers.md#object-tax_id) object.

**Request**

cURL:

```bash
curl https://api.fluveo.com/v1/customers/cus_123/tax_ids/txi_123 \
  -H "Authorization: Bearer sk_test_..."
```

Node.js:

```javascript
const response = await fetch("https://api.fluveo.com/v1/customers/cus_123/tax_ids/txi_123", {
  method: "GET",
  headers: {
    "Authorization": "Bearer sk_test_...",
  },
});
console.log(await response.json());
```

Python:

```python
import requests

response = requests.get(
    "https://api.fluveo.com/v1/customers/cus_123/tax_ids/txi_123",
    headers={"Authorization": "Bearer sk_test_..."},
)
print(response.json())
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/customers/cus_123/tax_ids/txi_123");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "GET",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_test_...",
    ],
]);
echo curl_exec($ch);
```

## Deletes a deleted tax id (Preview)

`DELETE /v1/customers/{customer}/tax_ids/{id}`

Permission: [`rak_customer_write`](https://docs.fluveo.com/permissions.md#customer).

**Preview:** this operation works, but it is not locked yet; it may change.

**Path parameters**

- `customer` (string, required): The Customer's id.
- `id` (string, required): The object's id.

**Headers**

- `Fluveo-Version` (enum, optional): Optional. The only version Fluveo serves is 2026-05-27.dahlia; any other value is refused with a 400. Every answer names that version in its own Fluveo-Version header, except the refusals Fluveo gives before it reads the request (no key, an unknown key, an unknown URL, a body that is not a form) and a file's bytes, which name no version and no Request-Id.

  One of: `2026-05-27.dahlia`.

**Request**

cURL:

```bash
curl -X DELETE https://api.fluveo.com/v1/customers/cus_123/tax_ids/txi_123 \
  -H "Authorization: Bearer sk_test_..."
```

Node.js:

```javascript
const response = await fetch("https://api.fluveo.com/v1/customers/cus_123/tax_ids/txi_123", {
  method: "DELETE",
  headers: {
    "Authorization": "Bearer sk_test_...",
  },
});
console.log(await response.json());
```

Python:

```python
import requests

response = requests.delete(
    "https://api.fluveo.com/v1/customers/cus_123/tax_ids/txi_123",
    headers={"Authorization": "Bearer sk_test_..."},
)
print(response.json())
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/customers/cus_123/tax_ids/txi_123");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "DELETE",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_test_...",
    ],
]);
echo curl_exec($ch);
```

*Response (abridged example)*

```json
{
  "deleted": true,
  "id": "txi_123",
  "object": "tax_id"
}
```

## The customer object

Ids start with `cus_`.

**Attributes**

- `address` (object, nullable)
- `balance` (integer)
- `business_name` (string)
- `cash_balance` (object, nullable)
- `created` (integer)
- `currency` (string, nullable)
- `customer_account` (string, nullable)
- `default_source` (string, nullable)
- `delinquent` (boolean, nullable)
- `description` (string, nullable)
- `email` (string, nullable)
- `id` (string)
- `individual_name` (string)
- `livemode` (boolean)
- `metadata` (object)
- `name` (string, nullable)
- `object` (enum): One of: `customer`.
- `phone` (string, nullable)
- `preferred_locales` (array, nullable)
- `shipping` (object, nullable)
- `sources` (object)
- `tax` (object)
- `tax_exempt` (enum, nullable): One of: `exempt`, `none`, `reverse`.
- `tax_ids` (object)
- `test_clock` (string, nullable)

## The tax_id object

Ids start with `txi_`.

**Attributes**

- `country` (string, nullable)
- `created` (integer)
- `customer` (string, nullable, expandable): An id by default; send `expand[]=customer` to get the full customer object.
- `customer_account` (string, nullable)
- `id` (string)
- `livemode` (boolean)
- `object` (enum): One of: `tax_id`.
- `owner` (object, nullable)
- `type` (enum): One of: `ad_nrt`, `ae_trn`, `al_tin`, `am_tin`, `ao_tin`, `ar_cuit`, `au_abn`, `au_arn`, `aw_tin`, `az_tin`, `ba_tin`, `bb_tin`, `bd_bin`, `bf_ifu`, `bg_uic`, `bh_vat`, `bj_ifu`, `bo_tin`, `br_cnpj`, `br_cpf`, `bs_tin`, `by_tin`, `ca_bn`, `ca_gst_hst`, `ca_pst_bc`, `ca_pst_mb`, `ca_pst_sk`, `ca_qst`, `cd_nif`, `ch_uid`, `ch_vat`, `cl_tin`, `cm_niu`, `cn_tin`, `co_nit`, `cr_tin`, `cv_nif`, `de_stn`, `do_rcn`, `ec_ruc`, `eg_tin`, `es_cif`, `et_tin`, `eu_oss_vat`, `eu_vat`, `fo_vat`, `gb_vat`, `ge_vat`, `gi_tin`, `gn_nif`, `hk_br`, `hr_oib`, `hu_tin`, `id_npwp`, `il_vat`, `in_gst`, `is_vat`, `it_cf`, `jp_cn`, `jp_rn`, `jp_trn`, `ke_pin`, `kg_tin`, `kh_tin`, `kr_brn`, `kz_bin`, `la_tin`, `li_uid`, `li_vat`, `lk_vat`, `ma_vat`, `md_vat`, `me_pib`, `mk_vat`, `mr_nif`, `mx_rfc`, `my_frp`, `my_itn`, `my_sst`, `ng_tin`, `no_vat`, `no_voec`, `np_pan`, `nz_gst`, `om_vat`, `pe_ruc`, `ph_tin`, `pl_nip`, `py_ruc`, `ro_tin`, `rs_pib`, `ru_inn`, `ru_kpp`, `sa_vat`, `sg_gst`, `sg_uen`, `si_tin`, `sn_ninea`, `sr_fin`, `sv_nit`, `th_vat`, `tj_tin`, `tr_tin`, `tw_vat`, `tz_vat`, `ua_vat`, `ug_tin`, `unknown`, `us_ein`, `uy_ruc`, `uz_tin`, `uz_vat`, `ve_rif`, `vn_tin`, `za_vat`, `zm_tin`, `zw_tin`.
- `value` (string)
- `verification` (object, nullable)
