> ## Documentation Index
> Fetch the complete documentation index at: https://docs.arcuserp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Creating Accounts with Addresses

> Create a complete B2B account — addresses, contacts, payment methods, and tags — in one atomic API call.

## Overview

The `POST /v1/accounts` endpoint accepts the full account onboarding graph in a single request. All children (addresses, contacts, payment methods, tags) are written in one database transaction. Any child failure rolls back the parent account and every already-inserted child — you get a complete record or nothing.

The 201 response includes all children hydrated inline; no follow-up GET calls are needed.

## Minimal create

Only `name` and `account_type` are required.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.arcuserp.com/v1/accounts \
    -H "Authorization: Bearer ark_live_ent_acme_xxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "account_type": "business",
      "name": "Acme Wholesale"
    }'
  ```

  ```typescript TypeScript SDK theme={null}
  import Arcus from "@arcuserp/sdk";

  const arcus = new Arcus({ apiKey: process.env.ARCUS_API_KEY });

  const account = await arcus.accounts.create({
    account_type: "business",
    name: "Acme Wholesale",
  });
  ```

  ```python Python SDK theme={null}
  import arcuserp

  client = arcuserp.ArcusERP(api_key=os.environ["ARCUS_API_KEY"])

  account = client.accounts.create(
      account_type="business",
      name="Acme Wholesale",
  )
  ```
</CodeGroup>

## Full B2B onboarding graph

One POST creates the account, 3 addresses, 2 contacts, a payment method, and 2 tags.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.arcuserp.com/v1/accounts \
    -H "Authorization: Bearer ark_live_ent_acme_xxxx" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: acme-wholesale-onboard-2026-05-12" \
    -d '{
      "account_type": "business",
      "name": "Acme Wholesale",
      "email": "ap@acme.example",
      "phone": "+1-555-0100",
      "credit_limit": 25000.00,
      "addresses": [
        {
          "type": "billing",
          "label": "HQ",
          "is_default_billing": true,
          "street1": "1 Acme Way",
          "city": "Austin",
          "region": "TX",
          "postal_code": "78701",
          "country": "US"
        },
        {
          "type": "shipping",
          "label": "Main Warehouse",
          "is_default_shipping": true,
          "street1": "100 Industrial Dr",
          "city": "Austin",
          "region": "TX",
          "postal_code": "78702",
          "country": "US"
        },
        {
          "type": "shipping",
          "label": "Branch Warehouse",
          "street1": "200 Branch Rd",
          "city": "Houston",
          "region": "TX",
          "postal_code": "77001",
          "country": "US"
        }
      ],
      "contacts": [
        {
          "first_name": "Jane",
          "last_name": "Doe",
          "email": "jane@acme.example",
          "phone": "+1-555-0101",
          "is_primary": true,
          "role": "AP"
        },
        {
          "first_name": "John",
          "last_name": "Smith",
          "email": "john@acme.example",
          "role": "Operations"
        }
      ],
      "payment_methods": [
        {
          "stripe_payment_method_id": "pm_card_visa",
          "is_default": true
        }
      ],
      "tags": ["VIP", "Wholesale-Tier-1"]
    }'
  ```

  ```typescript TypeScript SDK theme={null}
  const account = await arcus.accounts.create({
    account_type: "business",
    name: "Acme Wholesale",
    email: "ap@acme.example",
    phone: "+1-555-0100",
    credit_limit: 25000.0,
    addresses: [
      {
        type: "billing",
        label: "HQ",
        is_default_billing: true,
        street1: "1 Acme Way",
        city: "Austin",
        region: "TX",
        postal_code: "78701",
        country: "US",
      },
      {
        type: "shipping",
        label: "Main Warehouse",
        is_default_shipping: true,
        street1: "100 Industrial Dr",
        city: "Austin",
        region: "TX",
        postal_code: "78702",
        country: "US",
      },
      {
        type: "shipping",
        label: "Branch Warehouse",
        street1: "200 Branch Rd",
        city: "Houston",
        region: "TX",
        postal_code: "77001",
        country: "US",
      },
    ],
    contacts: [
      {
        first_name: "Jane",
        last_name: "Doe",
        email: "jane@acme.example",
        phone: "+1-555-0101",
        is_primary: true,
        role: "AP",
      },
      {
        first_name: "John",
        last_name: "Smith",
        email: "john@acme.example",
        role: "Operations",
      },
    ],
    payment_methods: [
      {
        stripe_payment_method_id: "pm_card_visa",
        is_default: true,
      },
    ],
    tags: ["VIP", "Wholesale-Tier-1"],
  });
  ```

  ```python Python SDK theme={null}
  account = client.accounts.create(
      account_type="business",
      name="Acme Wholesale",
      email="ap@acme.example",
      phone="+1-555-0100",
      credit_limit=25000.00,
      addresses=[
          {
              "type": "billing",
              "label": "HQ",
              "is_default_billing": True,
              "street1": "1 Acme Way",
              "city": "Austin",
              "region": "TX",
              "postal_code": "78701",
              "country": "US",
          },
          {
              "type": "shipping",
              "label": "Main Warehouse",
              "is_default_shipping": True,
              "street1": "100 Industrial Dr",
              "city": "Austin",
              "region": "TX",
              "postal_code": "78702",
              "country": "US",
          },
          {
              "type": "shipping",
              "label": "Branch Warehouse",
              "street1": "200 Branch Rd",
              "city": "Houston",
              "region": "TX",
              "postal_code": "77001",
              "country": "US",
          },
      ],
      contacts=[
          {
              "first_name": "Jane",
              "last_name": "Doe",
              "email": "jane@acme.example",
              "phone": "+1-555-0101",
              "is_primary": True,
              "role": "AP",
          },
          {
              "first_name": "John",
              "last_name": "Smith",
              "email": "john@acme.example",
              "role": "Operations",
          },
      ],
      payment_methods=[
          {
              "stripe_payment_method_id": "pm_card_visa",
              "is_default": True,
          }
      ],
      tags=["VIP", "Wholesale-Tier-1"],
  )
  ```
</CodeGroup>

**Expected 201 response** (truncated):

```json theme={null}
{
  "data": {
    "id": "a1b2c3d4-...",
    "account_type": "business",
    "display_name": "Acme Wholesale",
    "account_number": "CUST-00042",
    "credit_limit": 25000.0,
    "credit_balance": 25000.0,
    "addresses": [
      { "id": "...", "type": "billing",  "label": "HQ",             "line1": "1 Acme Way",      "is_default_billing": true,  ... },
      { "id": "...", "type": "shipping", "label": "Main Warehouse",  "line1": "100 Industrial Dr", "is_default_shipping": true, ... },
      { "id": "...", "type": "shipping", "label": "Branch Warehouse", "line1": "200 Branch Rd",  ...                          }
    ],
    "contacts": [
      { "id": "...", "first_name": "Jane", "last_name": "Doe",   "is_primary": true,  "title": "AP",         ... },
      { "id": "...", "first_name": "John", "last_name": "Smith", "is_primary": false, "title": "Operations",  ... }
    ],
    "payment_methods": [
      { "id": "...", "type": "card", "stripe_payment_method_id": "pm_card_visa", "last_four": "4242", "is_default": true, ... }
    ],
    "tags": [
      { "id": "...", "name": "VIP",              "slug": "vip" },
      { "id": "...", "name": "Wholesale-Tier-1", "slug": "wholesale_tier_1" }
    ]
  }
}
```

## Address field aliases

The endpoint accepts Stripe-style field names as aliases for the internal column names:

| Public API field | Internal column |
| ---------------- | --------------- |
| `street1`        | `line1`         |
| `street2`        | `line2`         |
| `region`         | `state`         |
| `postal_code`    | `zip`           |

Both sets of names are accepted; canonical names (`line1`, `state`, `zip`) are returned in all responses.

## Default billing and shipping flags

* Exactly one billing address may have `is_default_billing: true`.
* Exactly one shipping address may have `is_default_shipping: true`.
* If no address explicitly sets the flag, the **first** address of each type is auto-defaulted.
* Sending more than one `is_default_billing: true` (or `is_default_shipping: true`) returns 422.

## Contact role vs title

The `role` field in the API payload is stored internally as `title`. Both field names are accepted in
the request body; `title` is returned in responses.

## Payment methods: test mode

In test mode, use Stripe test card tokens:

* `pm_card_visa` -- Visa, succeeds
* `pm_card_mastercard` -- Mastercard, succeeds
* `pm_card_visa_chargeDeclined` -- always fails

Stripe must be configured for your entity. If Stripe is not configured, `payment_methods[]` will
return an error; omit the array if you are testing without Stripe.

## Credit limit

`credit_limit` is a JS number (float) in the response, never a Postgres NUMERIC string.
`credit_balance` on a new account equals `credit_limit` (no outstanding AR yet).

## Tags: auto-create

Tags supplied by name are auto-created for the entity if they do not exist. The name is
slugified (lowercased, non-alphanumeric chars replaced by `_`) to form the slug. Applying
the same tag name twice in `tags[]` is idempotent.

## Error envelope

All errors follow the canonical envelope:

```json theme={null}
{
  "error": "missing_required_field",
  "code": "missing_required_field",
  "type": "validation_error",
  "hint": "Account 'name' is required.",
  "param": "name"
}
```

Common validation errors:

| error code               | param                                         | description                                                  |
| ------------------------ | --------------------------------------------- | ------------------------------------------------------------ |
| `missing_required_field` | `name`                                        | name (or an alias) not provided                              |
| `invalid_enum`           | `account_type`                                | account\_type not one of: business, individual, vendor, lead |
| `invalid_enum`           | `addresses[N].type`                           | address type must be billing or shipping                     |
| `invalid_value`          | `addresses[N].country`                        | country must be ISO 3166-1 alpha-2                           |
| `invalid_value`          | `addresses[].is_default_billing`              | more than one billing default                                |
| `invalid_value`          | `addresses[].is_default_shipping`             | more than one shipping default                               |
| `invalid_value`          | `contacts[].is_primary`                       | more than one primary contact                                |
| `missing_required_field` | `payment_methods[N].stripe_payment_method_id` | PM token missing                                             |
| `forbidden_field`        | `entity_id`                                   | entity\_id may not be set in request body                    |

## Idempotency

Supply `Idempotency-Key: <unique-string>` to safely retry creates without duplicating records.
The key is honored for 24 hours.

## Related endpoints

* `GET /v1/accounts/:id?expand[]=addresses,contacts` -- retrieve with children expanded
* `POST /v1/accounts/:id/addresses` -- add an address to an existing account
* `POST /v1/accounts/:id/contacts` -- add a contact to an existing account
* `POST /v1/accounts/:id/payment-methods` -- attach a payment method to an existing account
* `POST /v1/accounts/:id/merge` -- merge two accounts
