Skip to main content

Customers

Read your customer list, look up by phone or email, view all orders for a customer.

Written by Support

Customers are end-users who placed an order on your storefront. They're created automatically at first checkout — there is no public sign-up flow at v1. Each customer is scoped to one store: the same phone number on two different stores creates two customer rows.

Deduplication: when a new checkout arrives, we match on store + phone. If that customer already exists, their email/address/wilaya are updated and the record is reused; otherwise a new one is created. Email is not used for dedup — historically merchants get many anonymous orders without an email.

When a customer is created through POST /v1/orders, the entire customer.name string is stored in first_name and last_name is left empty — the first/last split shown in the examples below never happens for API-created records. For an existing customer matched by phone, only first_name is overwritten and any existing last_name is left untouched. Split client-side if you need it.

GET /v1/customers

List customers in your store. Cursor-paginated.

Auth: any active platform key for the store (customers:read is granted by default and is not separately enforced at v1).

Query parameters

Param

Type

Notes

limit

int 1–200

Default 50

cursor

string

Opaque

phone

string

Exact match, case-insensitive

email

string

Exact match, case-insensitive

There are no date, free-text search, sort or is_banned filters at v1; results are always newest-id-first.

Request

curl 'https://api.dzbuild.app/v1/customers?limit=20' \
  -H "Authorization: Bearer $DZ_KEY"

Response 200

{
  "data": {
    "items": [
      {
        "id":           5578,
        "first_name":   "John",
        "last_name":    "Doe",
        "phone":        "0555000000",
        "email":        null,
        "wilaya_id":    16,
        "commune":      "Bab Ezzouar",
        "total_orders": 3,
        "total_spent":  4500,
        "is_banned":    false,
        "created_at":   "2026-03-17 15:18:13",
        "updated_at":   "2026-04-15 12:01:08"
      }
    ],
    "next_cursor": "NDk=",
    "has_more": true
  }
}

⚠️ Warning — total_orders / total_spent are stale counters, not live aggregates

They are only incremented when an order is placed from a landing page or created manually in the dashboard. They are never adjusted on a status change (a cancelled order still counts), and orders created via the storefront or POST /v1/orders never update them at all. Don't use them for revenue reporting — aggregate GET /v1/orders (or /v1/customers/{id}/orders) yourself.

GET /v1/customers/{id}

Full detail.

Auth: any active platform key for the store (customers:read is not separately enforced at v1).

Response 200

{
  "data": {
    "id":           5578,
    "first_name":   "John",
    "last_name":    "Doe",
    "phone":        "0555000000",
    "email":        "[email protected]",
    "wilaya_id":    16,
    "commune":      "Bab Ezzouar",
    "address":      "12 Rue X",
    "notes":        "Prefers afternoon delivery",
    "total_orders": 3,
    "total_spent":  4500,
    "fraud_score":  0,
    "is_banned":    false,
    "created_at":   "2026-03-17 15:18:13",
    "updated_at":   "2026-04-15 12:01:08"
  }
}

Field

Notes

notes

Merchant-private comment, set in dashboard

fraud_score

Always 0 at v1 — the field is reserved and never populated through the API. The risk indicator you see on the dashboard Customers page is not exposed here. Do not build fraud logic on this field.

is_banned

Set when you blacklist the customer in the dashboard. Storefront and landing-page checkouts reject banned customers — but POST /v1/orders does not check the blacklist, so API-created orders go through for banned customers. Check is_banned yourself before submitting.

Device / identity signals

Not exposed via API for privacy reasons

GET /v1/customers/{id}/orders

Customer's orders, cursor-paginated, newest first.

Auth: any active platform key for the store (customers:read is not separately enforced at v1).

The customer id is ownership-checked first, so an unknown id, or one belonging to another store, returns 404 not_found rather than an empty list.

Request

curl 'https://api.dzbuild.app/v1/customers/5578/orders?limit=10' \
  -H "Authorization: Bearer $DZ_KEY"

Response 200

{
  "data": {
    "items": [
      { "id": 6894, "order_number": "ORD-13-20260317-AD3C91F7", "status": "confirmed",
        "payment_status": "pending", "total": 1000, "created_at": "2026-03-17 15:18:13" }
    ],
    "next_cursor": null,
    "has_more": false
  }
}

This is a subset of /v1/orders filtered by customer_id — fields are the same as the orders list summary. To get the full order body call /v1/orders/{id}.

Common patterns

"Find a customer by phone, then list their orders"

PHONE="0555000000"
CUST=$(curl -sS "https://api.dzbuild.app/v1/customers?phone=$PHONE&limit=1" \
  -H "Authorization: Bearer $DZ_KEY" | jq -r '.data.items[0].id')[ -z "$CUST" ] && { echo "no customer"; exit 1; }curl -sS "https://api.dzbuild.app/v1/customers/$CUST/orders" \
  -H "Authorization: Bearer $DZ_KEY"

"Top 10 spenders this month"

There's no native sort-by-spend filter at v1. Walk the customer list and sort client-side; the dataset is small (typical store has < 5K active customers). Or query /v1/orders?since=... and aggregate by customer_id.

Privacy notes

  • The API never exposes device fingerprints or any cross-store identity link.

  • Customer email/phone are PII — be careful about logging them.

  • Future right-to-erasure (RGPD-style) requests should go through dashboard / support; the API will gain a DELETE /v1/customers/{id} endpoint in a future version with explicit cascade rules.

Did this answer your question?