Overview
ThePOST /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, so you get a complete record or nothing.
The 201 response includes all children hydrated inline; no follow-up GET calls are needed.
Minimal create
Onlyname is required. account_type is one of business, individual, vendor or lead, and it defaults to business, so send it whenever the account is anything else.
Anything you leave out is filled from your entity’s setup where Arcus can: the account takes the default payment term, the pricing level whose “defaults on” list names its account type, and the entity’s default location.
A second active account of the same type with the same name is refused with
422 duplicate_account_name, and the response lists the accounts it matched in existing. If you really want the duplicate, send "force_create": true.Full B2B onboarding graph
One POST creates the account, 3 addresses, 2 contacts, a payment method, and 2 tags.Address field aliases
The endpoint accepts Stripe-style field names as aliases for the internal column names:
Both sets of names are accepted; canonical names (
line1, state, zip) are returned in all responses.
Default billing and shipping flags
- At most one address in the request may have
is_default_billing: true, and at most one may haveis_default_shipping: true. Sending more than one of either returns 422. - A create call stores the flags exactly as you send them. Mark the addresses you want as defaults in the request; an address with no flag is saved as a plain billing or shipping address.
- To add an address later, call
POST /v1/accounts/:id/addresses. The first address an account ever gets becomes the default of its type automatically. After that, sendis_default_billing,is_default_shippingoris_defaultto take over the default; the previous default of that type is demoted. Settingis_pickup: trueforces the type toshipping. - The add-address call also checks the address when address validation is connected, and fills in
is_commercialand the coordinates when you did not send them. The create call does not run that check.
Contact title, role and primary
Each contact has two separate fields:titleis free text for the job title, such as “Accounts Payable”. It is saved by both the create call and the add-contact call.roleis a typed field that takes one ofprimary,ap,buyer,shipping,decision_maker,technicalorother(lowercase), or nothing. The add-contact call (POST /v1/accounts/:id/contacts) saves it and refuses any other value with400 contact_role_invalid. The inlinecontacts[]of a create call does not saverole, so set it afterward with the add-contact or update-contact call if you need it.
is_primary: true, the first one becomes primary. If you send no contacts at all, Arcus creates one primary contact from the account’s name, email and phone. Adding a contact with is_primary: true later demotes the current primary.
Payment methods: test mode
In test mode, use Stripe test card tokens:pm_card_visa— Visa, succeedspm_card_mastercard— Mastercard, succeedspm_card_visa_chargeDeclined— always fails
payment_method_attach_failed and no account is left behind; 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:Idempotency
SupplyIdempotency-Key: <unique-string> to safely retry creates without duplicating records.
The key is honored for 24 hours.
Related endpoints
GET /v1/accounts/:id— retrieve an account by its id or by its account number. The response already includes its addresses, contacts and payment methods, plus recent orders and a timeline, so no expand is needed. An id or number that matches nothing returns404 account_not_found. Needsaccounts:read.POST /v1/accounts/:id/addresses— add an address to an existing account. Acceptstype(oraddress_type),label,name,line1,line2,city,state,zip,country(defaultUS),is_commercial,is_default,is_default_billing,is_default_shippingandis_pickup, with the same aliases as above. Needsaccounts:write.POST /v1/accounts/:id/contacts— add a contact to an existing account:first_name,last_name,email,phone(orphone_main),title,role,websiteandis_primary. Needsaccounts:write.POST /v1/accounts/:id/payment-methods— attach a payment method to an existing account. Sendpayment_method_id, thepm_token from a Stripe SetupIntent that was already confirmed in the browser with Stripe.js. Needsaccounts:writeand a Stripe connection for the entity.POST /v1/accounts/:id/merge— merge a duplicate into this account. The account in the path is the one that survives; send the other asloser_account_idand, optionally, areason. Needs bothaccounts:writeandaccounts:delete.

