Supercommerce API Docs
Store API

Customer Module — Storefront

HTTP surface for the customer-side address book. The customer identity itself lives in Better-Auth's user table; this module owns the shopping-related customer data on top of that…

HTTP surface for the customer-side address book. The customer identity itself lives in Better-Auth's user table; this module owns the shopping-related customer data on top of that — currently just the address book.

Source: api-modules/customer/src/controllers/store-address.controller.ts.

The address book is read by the order module at place-order (for the shippingAddress / billingAddress snapshot) and by the cart for the address-bound shipping rate. The admin customer picker lives in docs/separated/admin/customer.md.


Conventions

Authentication

Endpoint groupAuth
GET/POST/PATCH/DELETE /store/addresses/**required (customer session)

Every read and write is scoped to the session's user.id. Cross-user ids return 404 Not Found, never 403 — so existence of another customer's address can't be inferred from the response code.

Response envelope

{
  "data": <payload>,
  "message": "Success",
  "statusCode": 200,
  "metadata": { /* optional, e.g. pagination */ }
}

Error envelope

statusCodeerrorCode examples
400BAD_REQUEST, VALIDATION_ERROR
401UNAUTHORIZED
404NOT_FOUND
500INTERNAL_SERVER_ERROR, DATABASE_ERROR

Domain types

AddressResponse

type AddressResponse = {
  id: string;
  firstName: string;
  lastName: string;
  fullAddress: string;           // 1..500 chars
  city: string;                  // 1..80 chars
  pincode: string;               // Indian 6-digit, /^[1-9]\d{5}$/
  state: string;                 // 1..80 chars
  phone: string;                 // Indian, /^(\+91)?[6-9]\d{9}$/
  country: string;               // ISO-3166 alpha-2; default "IN"
  isDefault: boolean;
  createdAt: string;             // ISO
  updatedAt: string;             // ISO
};

Phone and pincode validation is India-specific today; internationalization swaps these for a country-driven validator.

Default-address rule

A customer has at most one default address. The address book invariants:

TriggerBehavior
First-ever address (book is empty)Force-promoted to isDefault: true regardless of input
Subsequent create with isDefault: trueNew row becomes default; prior default flips to false
POST /:id/defaultSame — flip swap
Delete defaultNo auto-promotion — caller is left without a default until they explicitly pick one. Silent re-promotion can ship to a stale address.

Endpoints

GET /store/addresses — List my addresses

Default address comes first; the rest are sorted by most-recently-touched.

Query

NameTypeDefaultConstraints
pageint1>= 1
limitint201..100

The service converts page-based pagination to offset internally; the response uses the standard { data, metadata: { total, limit, offset, hasMore } } envelope.

Response 200 — paginated AddressResponse[].


GET /store/addresses/:id — Get one of my addresses

Response 200AddressResponse.

Errors

StatusCodeWhen
404NOT_FOUNDAddress does not exist or belongs to another user

POST /store/addresses — Create an address

Body

{
  "firstName": "Ada",
  "lastName": "Lovelace",
  "fullAddress": "221B Baker Street",
  "city": "London",
  "pincode": "110001",                 // 6-digit, first digit 1-9
  "state": "Delhi",
  "phone": "+919876543210",            // +91 prefix optional
  "country": "IN",                     // ISO-3166 alpha-2; default "IN"
  "isDefault": false
}

Response 201AddressResponse. If this is the caller's first address, the response has isDefault: true even when the body sent false.

Errors

StatusCodeWhen
400VALIDATION_ERRORBody fails zod (phone/pincode regex, missing required field)

PATCH /store/addresses/:id — Update an address

Body — partial CreateAddressInput. At least one field must be present (no-op writes are rejected at the zod layer to avoid a misleading 200).

{ "city": "Mumbai", "pincode": "400001" }

Response 200 — updated AddressResponse.

Errors

StatusCodeWhen
400VALIDATION_ERROREmpty body; any field fails its individual schema
404NOT_FOUNDAddress not owned by caller

DELETE /store/addresses/:id — Delete an address

Hard delete. Deleting the default address leaves the customer without a default until they explicitly pick one — no auto-promotion.

Response 204 No Content.

Errors

StatusCodeWhen
404NOT_FOUNDAddress not owned by caller

POST /store/addresses/:id/default — Set as default

Flips the chosen address to isDefault: true and the prior default (if any) to false. Idempotent — already-default is a no-op 200.

Response 200 — the new default AddressResponse.

Errors

StatusCodeWhen
404NOT_FOUNDAddress not owned by caller

  • auth — owns the user table that the address book references via userId.
  • order — snapshots shippingAddress / billingAddress from this module at place-order. See order.md.
  • cartPATCH /store/cart/address accepts the address id from this module to pin the cart's delivery target. See cart.md.

On this page