MeyktAPI Documentation
OpenAPI specllms-full.txtDashboard
  • Overview
  • Quickstart
  • Authentication
  • Orders
  • Customers
  • Files
  • Webhooks
  • Articles & variants
  • Goods conditions
  • Sets & stock structures
  • Manufacturing
  • Inbound deliveries
  • Returns
  • Inventory
  • Shipping
  • Integration profiles
  • Errors

Customers

Exchange your organization's customer records with your own system. This is a separate resource from the customer name and addresses inside an order. Customer access covers your organization's customer directory, including customers created through inquiries; it is not limited to customers created through this API connection.

#Permission and sharing approval

All three endpoints require a valid API key and the organization's current approval to share customer data. The owner or an administrator grants or withdraws that approval under Integrations → API access → Share customer data with your system. It applies to both reading and writing, including validation-only requests. Without it the API returns 403 customer_bridge_not_approved.

Method and pathRequired scopeTest keyLive key
GET /v1/customerscustomers:readReads real customer records after approvalReads customer records after approval
GET /v1/customers/{id}customers:readReads real customer records after approvalReads customer records after approval
POST /v1/customerscustomers:writeOnly validate_only: true; no saved changesValidate or create/update

Write does not imply read. Order permissions do not grant customer-directory access. Unlike orders, customers have no test flag or separate test dataset: a test key's reads are real, and its ordinary POST is rejected with 403 test_key_read_only.

Revoking sharing blocks subsequent customer calls. It cannot recall data already fetched by your system. There is no customer webhook or automatic outbound customer delivery; your system reads changes when needed.

#Create or update a customer

POST /v1/customers — requires customers:write and sharing approval.

The same endpoint creates or updates a record. There is no separate customer PATCH endpoint. Matching proceeds in this order:

  1. external_reference: exactly one matching reference anywhere in your organization's customer directory.
  2. code: the Meykt customer number, including an inactive customer with that code.
  3. email: case-insensitive matching against active customers only, after trimming and lowercasing.
  4. No match: create a new customer.

Names alone are not matching keys: repeating only a display name can create another customer. Retain a stable external reference or known customer code rather than relying on a name for duplicate protection.

A higher-priority match determines the target. Multiple matches for the external reference or email return 409 customer_match_ambiguous; Meykt does not choose a customer for you. Use a stable reference or the known customer code to disambiguate. Do not assume that supplying a different email forces a new record when the external reference already matches.

These references are organization-wide, not a separate namespace per key or connection. If several source systems share the same directory, coordinate their references; for example, use a stable system prefix in external_reference. A newly supplied reference is filled in when the matched customer has none. An existing reference is never silently replaced. It is not returned in customer responses, so retain your source-to-Meykt mapping yourself.

Every request, including an update, needs at least one nonempty name, company, last_name or email. A reference or code alone is insufficient. Ordinary string inputs are trimmed and must be nonempty when supplied; JSON null and empty strings are not a way to clear them.

#Validate before writing

Save this fictional example as customer.json. Start with a test key and validate_only: true:

json
{
  "external_reference": "ERP-CUSTOMER-42",
  "code": "C-2042",
  "name": "Example GmbH",
  "customer_type": "b2b",
  "company": "Example GmbH",
  "first_name": "Erika",
  "last_name": "Muster",
  "email": "erika@example.invalid",
  "phone": "+49 30 5550100",
  "street": "Example Street",
  "house_number": "7",
  "postal_code": "10115",
  "city": "Berlin",
  "country": "DE",
  "preferred_content_language": "de",
  "custom_fields": { "segment": "Wholesale" },
  "delivery_address": {
    "company": "Example GmbH",
    "street": "Warehouse Road",
    "house_number": "2",
    "postal_code": "10115",
    "city": "Berlin",
    "country": "DE"
  },
  "contacts": [{
    "first_name": "Erika",
    "last_name": "Muster",
    "position": "Purchasing",
    "email": "erika@example.invalid",
    "is_primary": true
  }],
  "update_mode": "overwrite",
  "validate_only": true
}
bash
curl -X POST https://api.meykt.com/v1/customers \
  -H "Authorization: Bearer $MEYKT_API_KEY" \
  -H "Content-Type: application/json" \
  -d @customer.json

For a previously unknown customer, the validation response is 200:

json
{"data":{"result":"would_create","matched_by":null,"customer_id":null}}

A matching record instead yields would_update, its customer_id, and matched_by equal to external_reference, code or email. Validation saves nothing and does not reserve a customer number or freeze the decision. The result can change if another writer changes the directory before your live request.

To save, use a live key and set validate_only to false or omit it. A creation returns 201 with result: "created"; an update returns 200 with result: "updated". Both include data.customer and nullable data.matched_by. Retain the returned customer id and code.

#Request fields and limits

FieldAccepted input and behavior
external_referenceString, 1–200 characters; preferred source identity, not a returned field
codeString, 1–60; matches an existing Meykt customer or selects the number for a new one; omitted on creation means Meykt assigns one
nameString, 1–300; creation display name and required identity input, not a matching key; does not rename a matched customer's display name
salutationmr, mrs, diverse, dr, prof
first_name, last_nameString, 1–120 each
emailEmail address, at most 254; trimmed and normalized for matching
phone, mobileString, 1–60 each
companyString, 1–200
customer_typeb2c, b2b, wholesale; defaults to b2c only for creation
street, address_line2String, 1–200 each; billing address input at the top level
house_numberString, 1–40
postal_codeString, 1–30; preserve leading zeroes
cityString, 1–120
countryString, 1–80; unlike the order address contract, this is not restricted to a two-letter value
website_urlString, 1–300
company_sizeString, 1–60
preferred_content_languageTwo-character string; supply a valid two-letter language code such as de or en
custom_fieldsObject of additional customer-field values, keys at most 80 characters; not arbitrary customer metadata
delivery_addressOptional nonempty object; complete replacement of the default delivery address
contactsOptional list of 1–20 complete contacts; replaces the contact list
update_modeoverwrite (default) or fill_empty
validate_onlyBoolean; true evaluates matching without saving

The complete JSON request is limited to 256 KiB, 32 nesting levels and 25000 values. No customer ID, organization ID, is_active, source, order revision or order-command idempotency field is writable through this contract. Send only documented fields.

#Update and omission rules

overwrite applies supplied nonempty ordinary contact and billing-address fields. Omitted fields stay as they are. fill_empty only fills currently empty ordinary fields; it preserves existing values in those fields.

The following rules are separate from that ordinary-field policy:

  • name and code are used for creation/identification and do not rename or renumber a matched customer. Use Meykt for those edits. Supplying only a name does not change an existing display name.
  • An explicitly supplied customer_type is applied even in fill_empty; omission preserves the current type.
  • custom_fields merges the supplied keys into existing custom fields. A supplied key can replace its previous value even with fill_empty; omitted keys remain. {} does not clear all custom fields.
  • A supplied delivery_address replaces the whole default delivery address, including clearing omitted fields in that object, even with fill_empty. Omit the object to preserve the address. {} and null are invalid.
  • A supplied contacts list replaces the entire contact list, even with fill_empty. Omit it to preserve existing contacts. [] and null are invalid.

The delivery-address object accepts company, first_name, last_name, street, house_number, address_line2, postal_code, city, and country with the same lengths as above. A contact requires both first_name and last_name; optional fields are salutation, position (1–120), email (up to 254), phone, mobile (up to 60 each), and is_primary. The first submitted contact is marked primary; other supplied primary flags are retained. Reads return primary contacts first and then by creation time. Contact and address IDs are not exposed for individual patching.

For example, with a live key and an existing matching reference, this updates the phone and leaves omitted address/contact sections untouched. The required identity name does not rename the customer:

json
{
  "external_reference": "ERP-CUSTOMER-42",
  "name": "Example GmbH",
  "phone": "+49 30 5550101",
  "update_mode": "overwrite"
}

Matching stops at the first decisive level. If external_reference matches customer A, a supplied code belonging to B does not override A or create an identity-conflict response; the existing code is not renamed. If that external reference is itself ambiguous, supplying a code alongside it does not bypass the ambiguity. Resolve the reference, or deliberately omit it to match by code after checking the preview.

#Retry and concurrency

This is a matching/upsert API, not the order-command journal. It has no immutable idempotency UUID, revision precondition or separate test/live customer namespace. A sequential repeat with the same stable reference normally updates the same customer and returns 200, rather than replaying an immutable 201 result. Changed content can update that customer instead of producing an order-style content conflict.

Serialize writes for a source customer. Do not assume exactly-once creation under concurrent requests or reuse one reference for different people. If a timeout or 500 occurs, a customer may already have been saved, or some updates may have applied before a later failure. Read/reconcile the known customer and repeat with the same reference/code; do not manufacture a new identity to escape an uncertain outcome.

#Read one customer

GET /v1/customers/{id} — requires customers:read and sharing approval. {id} is the Meykt customer UUID, not the code or your external reference. An inactive customer can be read directly when its ID is known.

bash
curl https://api.meykt.com/v1/customers/0f150bf1-cbe2-43a9-9f19-60394450c701 \
  -H "Authorization: Bearer $MEYKT_API_KEY"

The response is 200 with data.customer. The same complete customer shape is returned after a successful write:

json
{
  "data": {
    "customer": {
      "id": "0f150bf1-cbe2-43a9-9f19-60394450c701",
      "code": "C-2042",
      "name": "Example GmbH",
      "salutation": null,
      "first_name": "Erika",
      "last_name": "Muster",
      "email": "erika@example.invalid",
      "phone": "+49 30 5550100",
      "mobile": null,
      "company": "Example GmbH",
      "customer_type": "b2b",
      "billing_address": { "street": "Example Street", "house_number": "7", "address_line2": null, "postal_code": "10115", "city": "Berlin", "country": "DE" },
      "website_url": null,
      "company_size": null,
      "preferred_content_language": "de",
      "is_active": true,
      "custom_fields": { "segment": "Wholesale" },
      "delivery_address": { "company": "Example GmbH", "first_name": null, "last_name": null, "street": "Warehouse Road", "house_number": "2", "address_line2": null, "postal_code": "10115", "city": "Berlin", "country": "DE" },
      "contacts": [{ "salutation": null, "first_name": "Erika", "last_name": "Muster", "position": "Purchasing", "email": "erika@example.invalid", "phone": null, "mobile": null, "is_primary": true }],
      "created_at": "2026-10-19T09:00:00Z",
      "updated_at": "2026-10-19T09:00:00Z"
    }
  }
}

Optional record values can be null; a missing default delivery address is delivery_address: null, and no contacts is contacts: []. Responses omit the source reference and private link/organization metadata. Foreign, unknown and malformed customer IDs answer the same 404 not_found, once key/scope/consent checks pass.

#List and reconcile customers

GET /v1/customers — requires customers:read and sharing approval.

bash
curl 'https://api.meykt.com/v1/customers?include_inactive=true&limit=100' \
  -H "Authorization: Bearer $MEYKT_API_KEY"
QueryMeaning
limitPage size, default 25; numeric values are truncated/clamped to 1–100
starting_afterLast returned customer's id, supplied from next_cursor
updated_sinceTimestamp; returns customers changed at or after it
include_inactiveExactly true includes inactive customers; default excludes them

The result is {data:{customers:[...],has_more,next_cursor}}. Every member has the complete customer shape above. next_cursor is a customer ID when more pages exist, otherwise null; pass it as starting_after while keeping the other filters fixed. A malformed, unknown or foreign cursor and an invalid timestamp return a neutral 422 validation_failed. A failed cursor lookup is a 500 internal_error; keep the previous cursor and retry instead of treating it as an empty page.

Include inactive customers when reconciling status. Otherwise an archived record disappears from the active list without a deletion notice. This endpoint has no tombstone stream or customer webhook; it is not the inventory cursor protocol. Do not treat an absent result or failed page as a deletion. Use overlapping update windows and periodic reconciliation rather than advancing a watermark before all pages have been stored.

Customers are ordered by creation time descending, then ID descending. Continuation uses both values, so customers sharing the same creation timestamp are included across page boundaries. Keep the returned ID cursor intact; do not reconstruct it from rounded timestamps. This is keyset pagination, not a frozen snapshot: keep filters fixed across a read and reconcile subsequent updates separately.

#Errors and recovery

HTTP / codeMeaning and remedy
401 invalid_api_keyCheck the key, connection, expiry and revocation before retrying
403 insufficient_scopeExplicitly grant the required customer read/write scope on a new key
403 customer_bridge_not_approvedAn owner/admin must approve current customer sharing in Meykt
403 test_key_read_onlyUse validate_only:true, or explicitly switch to an authorized live key to save
422 validation_failedFix missing identity, invalid values, empty replacement address/contact list or invalid read filters; POST reports its first validation issue
409 customer_match_ambiguousSupply a known code/reference or resolve ambiguous records; never guess a target
409 customer_code_takenA creation could not claim its supplied code; reconcile the existing customer and intended identity
404 not_foundThe single customer is unavailable to this organization, or the endpoint is wrong
429 rate_limitedHonor Retry-After; all endpoints share the key's 120-request/minute limit
500 internal_errorRead/reconcile the known customer before retrying an uncertain write; a failed response is not proof that nothing was saved

For shared JSON/body errors and key handling see Errors and Authentication. Customer-directory updates do not modify the address snapshot of an existing order; use the order's own change command when that is the intended action. Customer operations are not offered as integration-profile operations in the current capability catalog.

Machine-readable version of this page: https://www.meykt.com/developers/customers.md

On this page

  • Permission and sharing approval
  • Create or update a customer
  • Validate before writing
  • Request fields and limits
  • Update and omission rules
  • Retry and concurrency
  • Read one customer
  • List and reconcile customers
  • Errors and recovery

Base URL https://api.meykt.com/v1 · Machine-readable: openapi.json · llms-full.txt

Imprint · Privacy · Terms