Zum Hauptinhalt springen
Omneya
EN
Start test system Try it
Developer docs menu

Maintain customers

Read a customer and keep addresses, notes, labels and the customer class up to date, using your own customer ID.

Which customers you can reach

The customer endpoints work with customers that were created or matched through your access, see How customers are matched. You address them with your customer ID, the customer.external_id from your orders:

GET /api/v1/customers/u-55871

Any other ID, a customer linked to a different access, and a deleted customer all answer 404 not_found. The response does not reveal whether such a customer exists.

The ID is part of the path, so URL-encode it if it contains special characters. Do not use / in customer IDs you want to address here.

Scopes:

ScopeAllows
customers.readread the customer, the address book, the list of labels and the list of customer classes
customers.writecreate, change and delete addresses, create notes, set labels and the customer class

See the scope reference. Each shop has its own address; the examples use https://shop.example.com/api/v1.

Read a customer

GET /customers/{external_id} returns the complete customer record and the address book. Login data such as passwords and security codes is never included.

curl -s https://shop.example.com/api/v1/customers/u-55871 \
  -H "Authorization: Bearer $OMNEYA_API_KEY" \
  -H "Accept: application/json"

The response is shortened here. It contains all stored fields of the customer, and the set of fields can grow:

{
  "data": {
    "customer_id": 9021,
    "external_id": "u-55871",
    "email": "jane.doe@example.com",
    "first_name": "Jane",
    "last_name": "Doe",
    "country": "DE",
    "billing": {
      "salutation": "Ms",
      "title": null,
      "first_name": "Jane",
      "last_name": "Doe",
      "company": null,
      "street": "Example Street",
      "house": "5",
      "additional": "2nd floor",
      "postcode": "10115",
      "city": "Berlin",
      "country": "DE"
    },
    "addresses": [
      {
        "id": 1,
        "first_name": "Jane",
        "last_name": "Doe",
        "street": "Packstation",
        "house_number": "123",
        "postcode": "10117",
        "city": "Berlin",
        "country": "DE",
        "packstation": 1,
        "postnumber": "12345678",
        "is_default_shipping": true
      }
    ]
  }
}

billing uses the same field names as orders. Entries in addresses contain the stored fields of each address, plus country as a two-letter code and is_default_shipping.

Addresses

ActionRequestSuccess
List addressesGET /customers/{external_id}/addresses200
Create an addressPOST /customers/{external_id}/addresses201, or 200 if the same address already exists
Update an addressPATCH /customers/{external_id}/addresses/{id}200
Delete an addressDELETE /customers/{external_id}/addresses/{id}204

The request body uses the same format as customer.shipping in an order:

curl -s -X POST https://shop.example.com/api/v1/customers/u-55871/addresses \
  -H "Authorization: Bearer $OMNEYA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  --data '{
    "first_name": "Jane",
    "last_name": "Doe",
    "street": "Packstation",
    "house": "123",
    "postcode": "10117",
    "city": "Berlin",
    "country": "DE",
    "telephone": "+49 30 1234567",
    "packstation": true,
    "postnumber": "12345678",
    "is_default_shipping": true,
    "is_default_billing": false
  }'

How the address endpoints behave:

  • Creating an address that already exists for the customer creates no second one. You receive the existing address with 200.
  • is_default_shipping and is_default_billing make the address the default delivery or billing address. The previous default loses its flag, so there is always at most one of each.
  • When updating, send only the fields that change. street, postcode, city and country cannot be emptied.
  • The response contains the stored address with its id, which you need for updates and deletes.
  • An id that does not belong to this customer answers 404.

All fields and limits are in the address reference.

Notes

POST /customers/{external_id}/notes adds a note to the customer, for staff in the shop:

{ "text": "Customer prefers delivery in the afternoon.", "notification": 0 }

notification controls whether staff see the note as a notice: 0 no notice (default), 1 notice when opening the customer or an order and when creating an order, 2 notice only in the customer view.

text may contain plain text or simple HTML such as paragraphs, lists and bold text. Scripts and event attributes are removed. Special characters come back HTML-encoded, for example & as &.

The response answers 201:

{
  "data": {
    "id": 501,
    "customer_id": 9021,
    "text": "Customer prefers delivery in the afternoon.",
    "notification": 0,
    "created_at": "2026-10-13T14:02:11+02:00"
  }
}

Each request creates a new note.

Labels

Labels are defined in the shop. Each label has values, and you assign values, not labels. GET /customer-labels lists all labels with their values:

{
  "data": [
    {
      "id": 4,
      "name": "Interests",
      "active": true,
      "values": [
        { "id": 31, "value": "Running", "color": "#2e7d32", "active": true },
        { "id": 32, "value": "Yoga", "color": null, "active": true }
      ]
    }
  ]
}

PUT /customers/{external_id}/labels sets values:

{ "mode": "add", "value_ids": [31, 32] }
  • add (default) adds the values; values already set stay.
  • replace sets exactly these values and removes all others. "value_ids": [] removes all.

A value that is already set is never added twice. The response lists all values set afterwards:

{ "data": { "customer_id": 9021, "value_ids": [31, 32] } }

DELETE /customers/{external_id}/labels/{value_id} removes one value and answers 204. If the value was not set, it answers 404.

Customer class

GET /customer-classes lists all customer classes with their settings, for example discount and price display, and under payment_methods the payment methods assigned to each class.

PUT /customers/{external_id}/class assigns a class:

{ "customer_class_id": 3 }
{ "data": { "customer_id": 9021, "customer_class_id": 3 } }

The new class applies from the customer’s next order on, including its discount.

Errors

Invalid input answers 422 validation_failed with all problems per field, in the same format as orders:

{
  "error": {
    "code": "validation_failed",
    "message": "The request payload is invalid.",
    "details": {
      "postcode": [{ "code": "required", "message": "The postcode field is required." }]
    }
  }
}

Typical field codes here: required, unknown_country for an unknown country code, unknown_value for an unknown label value or customer class, invalid_value for an unknown mode or notification, duplicate_value for a value listed twice in value_ids, and out_of_range for more than 200 entries in value_ids. See Errors.