Guide

Customer profile, marketing consent, and payment methods

Read and update a customer's profile, capture marketing consent in a GDPR-defensible way, manage their labels, and work with the payment methods Trybe holds on file. This guide covers the two sides of the customer surface — back-office operations done with a personal access token, and self-service operations done with an authenticated customer's own token.

Before you start

  • For back-office operations, a personal access token with permission to manage customers on the target site. See authentication for the header format.
  • For self-service operations, a customer access token obtained through the SSO flow. The self-service endpoints (/customers/my-account/* and /shop/my-account/*) only respond to customer tokens — staff tokens are explicitly rejected.
  • A Customer to operate on. Customers belong to a brand; the API rejects cross-brand operations.
  • A webhook configuration listening for customer.created, customer.updated, customer.deleted, and the related customer_address.*, customer_labels.*, and customer_note.* events if you want to keep a downstream CRM in sync. See the webhooks guide.

The shape of the customer surface

The customer domain splits into two parallel API surfaces:

  • Back-office (/customers/customers/{customerId}/..., /customers/labels, /customers/marketing-preferences, /customers/payment-methods). Use these with a personal access token whose user has the relevant permissions. The token acts on behalf of the operator, and permission checks are performed against the operator's role.

  • Self-service (/customers/my-account/..., /shop/my-account/...). These endpoints take the authenticated customer's bearer token and return only that customer's data. There is no customer_id path parameter — the customer is implicit in the token.

Use the back-office surface for staff workflows and CRM imports; use the self-service surface for the customer-facing storefront. Mixing them up is the most common authentication mistake against this domain — a back-office token on a self-service endpoint returns 401, and a customer token on a back-office endpoint returns either 401 or 403 depending on the route.

1. Reading and updating the customer

Fetch a customer by ID with getCustomer:

curl "https://api.playground.try.be/customers/customers/$CUSTOMER_ID" \
  -H "Authorization: Bearer $TRYBE_API_KEY" \
  -H "Accept: application/json"
const res = await fetch(
  `https://api.playground.try.be/customers/customers/${customerId}`,
  {
    headers: { Authorization: `Bearer ${process.env.TRYBE_API_KEY}` },
  },
)
const { data: customer } = await res.json()

The response is the full Customer record — name, email, phone, date of birth, gender, addresses, the brand they belong to, marketing preferences (summary form), and timestamps. The brand attribution is the most important field to keep in mind: a customer with the same email on two different brands is two separate records, and the API will not merge them.

Update with PUT:

curl "https://api.playground.try.be/customers/customers/$CUSTOMER_ID" \
  -X PUT \
  -H "Authorization: Bearer $TRYBE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Jane",
    "last_name": "Doe",
    "phone": "+447700900000"
  }'
$response = Http::asJson()
    ->withToken(config('services.trybe.token'))
    ->put("https://api.playground.try.be/customers/customers/{$customerId}", [
        'first_name' => 'Jane',
        'last_name'  => 'Doe',
        'phone'      => '+447700900000',
    ]);

updateCustomer is a full update — pass every field you want to keep, not just the changed ones, unless your write strategy is genuinely patch-style. The endpoint accepts partial bodies and merges, but the server-side resolution of phone normalisation, address re-geocoding, and label retention is easier to reason about when the body is explicit.

The customer can also be deleted (anonymised); the record is kept for accounting and reporting but PII is scrubbed. See getCustomer, updateCustomer, and deleteCustomer.

2. Self-service: My Account

For storefront flows where the customer is signed in via SSO and is managing their own profile, switch to the my-account surface. The authenticated customer is implicit, so no customer_id is sent.

Profile summary

curl https://api.playground.try.be/customers/my-account \
  -H "Authorization: Bearer $CUSTOMER_TOKEN" \
  -H "Accept: application/json"

Returns the same Customer payload as getCustomer for the authenticated customer. See getMyAccountDetails.

Order history

curl https://api.playground.try.be/shop/my-account/orders \
  -H "Authorization: Bearer $CUSTOMER_TOKEN" \
  -H "Accept: application/json"

Returns the customer's submitted-and-settled orders — completed baskets with item details — for the "My Bookings" screen. See listMyAccountOrders.

Memberships, credits, charges

The same pattern applies for the customer's memberships, credit balance, and membership charge history:

curl https://api.playground.try.be/customers/my-account/memberships \
  -H "Authorization: Bearer $CUSTOMER_TOKEN"

curl https://api.playground.try.be/customers/my-account/credits \
  -H "Authorization: Bearer $CUSTOMER_TOKEN"

curl https://api.playground.try.be/customers/my-account/charges \
  -H "Authorization: Bearer $CUSTOMER_TOKEN"

See listMyAccountMemberships, listMyAccountCredits, and listMyAccountCharges.

These four endpoints (orders, memberships, credits, charges) are the backbone of the customer self-service area. Wire them up in a tabbed "My Account" page and you've covered most of what a customer wants to see.

3. Labels

Labels are ad-hoc tags attached to customers for segmentation — "VIP", "Local", "Bridal party", "Returned in 2025". They're managed at the organisation level and applied at the customer level.

Manage the catalogue

curl https://api.playground.try.be/customers/labels \
  -H "Authorization: Bearer $TRYBE_API_KEY"
curl https://api.playground.try.be/customers/labels \
  -X POST \
  -H "Authorization: Bearer $TRYBE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "VIP", "colour": "#9999ff" }'
curl "https://api.playground.try.be/customers/labels/$LABEL_ID" \
  -X PUT \
  -H "Authorization: Bearer $TRYBE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "VIP guests" }'

Labels are soft-deleted, not hard-deleted — deleteLabel archives the label and removes it from the active picker; restoreLabel brings it back, complete with its prior attachments. The webhook events customer_labels.added and customer_labels.removed carry the full label data so downstream systems can build their own segmentation views.

See listLabels, createLabel, updateLabel, deleteLabel, and restoreLabel.

Attach to a customer

Label attachments live on the customer's record (a customer_labels collection) and are managed through the customer-scoped label routes exposed at /customers/customers/{customerId}/labels. Subscribe to customer_labels.added / customer_labels.removed for asynchronous notification.

This is the area where getting the API right matters most for GDPR compliance. Three things have to be true:

  1. Explicit opt-in. Consent is granted by the customer through an affirmative action, not by pre-ticked boxes or "by booking you consent to..." clauses.
  2. Auditable record. The platform records when and by which mechanism each opt-in was granted, so you can demonstrate compliance if asked.
  3. Easy opt-out. The customer can opt out at any time, and the opt-out takes effect immediately.

The API supports all three. The data model has two layers: the marketing preferences themselves (the channels and topics — "Email newsletter", "SMS offers", etc., configured per organisation), and the opt-ins (the customer-level grants against each preference).

Manage the catalogue

curl https://api.playground.try.be/customers/marketing-preferences \
  -H "Authorization: Bearer $TRYBE_API_KEY"
curl "https://api.playground.try.be/customers/marketing-preferences/$PREF_ID" \
  -H "Authorization: Bearer $TRYBE_API_KEY"
curl "https://api.playground.try.be/customers/marketing-preferences/$PREF_ID" \
  -X PUT \
  -H "Authorization: Bearer $TRYBE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Monthly newsletter", "is_active": true }'

See listMarketingPreferences, getMarketingPreference, and updateMarketingPreference.

The customer-facing opt-in / opt-out lives on the my-account surface:

curl "https://api.playground.try.be/customers/my-account/marketing-preferences/$PREF_ID" \
  -X POST \
  -H "Authorization: Bearer $CUSTOMER_TOKEN"
curl "https://api.playground.try.be/customers/my-account/marketing-preferences/$PREF_ID" \
  -X DELETE \
  -H "Authorization: Bearer $CUSTOMER_TOKEN"
async function setConsent(prefId, optedIn) {
  const url = `https://api.playground.try.be/customers/my-account/marketing-preferences/${prefId}`
  const res = await fetch(url, {
    method: optedIn ? 'POST' : 'DELETE',
    headers: { Authorization: `Bearer ${customerToken}` },
  })
  if (!res.ok) throw new Error('Failed to update consent')
  return res.json()
}

The opt-in is recorded with a timestamp and a source (the my-account endpoint, the basket flow, or a back-office manual change) so the provenance is auditable. See actionMyAccountMarketingPreferenceOptIn and actionMyAccountMarketingPreferenceOptOut.

Subscribe to marketing_preference_opt_in.updated (see the webhooks guide) to keep your downstream marketing-automation systems in sync with the source-of-truth state.

5. Payment methods

Payment methods in Trybe are tokenised references to cards or mandates held by the underlying processor (Stripe, GoCardless, etc.). The platform never sees the raw PAN, IBAN, or expiry; only the token and a non-sensitive summary (last 4 digits, brand, country).

Back-office listing

curl "https://api.playground.try.be/customers/payment-methods?customer_id=$CUSTOMER_ID" \
  -H "Authorization: Bearer $TRYBE_API_KEY"

Filter by customer_id, type, processor, status, membership_payment_method (boolean), or for_barcode. The result is paginated and ordered by creation date. See listPaymentMethods.

Self-service listing

curl https://api.playground.try.be/customers/my-account/payment-methods \
  -H "Authorization: Bearer $CUSTOMER_TOKEN"

Returns the customer's saved methods. See listMyAccountPaymentMethods.

Deletion

curl "https://api.playground.try.be/customers/payment-methods/$PAYMENT_METHOD_ID" \
  -X DELETE \
  -H "Authorization: Bearer $TRYBE_API_KEY"

Deletion is a soft detach — the underlying processor token is revoked and the method is no longer offered to the customer. See deletePaymentMethod.

Adding a method

There is no createPaymentMethod endpoint, by design. New methods are captured as a side-effect of taking a payment through the basket flow or signing a direct-debit mandate through the memberships flow. The processor's hosted Checkout page (Stripe Checkout, GoCardless Checkout) is responsible for capturing the bank or card details and tokenising them; Trybe receives the token and stores the method record. Build your "add a new card" UX as a basket-driven flow with a zero-value reserve or a small refundable hold, not as a standalone form.

6. Subscribing to outcomes

The customer domain is rich with webhook events. The ones most integrations care about:

  • customer.created, customer.updated, customer.deleted — the basic CRUD events. data is a Customer shape (or the deleted ID).
  • customer_address.created, customer_address.updated, customer_address.deleted — address-level changes.
  • customer_labels.added, customer_labels.removed — label attachments.
  • customer_note.created, customer_note.updated, customer_note.deleted — operator notes attached to the customer record.
  • marketing_preference_opt_in.updated — the moment of truth for marketing consent. Subscribe to this and use it (not the customer.updated event) to drive your marketing-automation audience membership.

All of these are documented in the webhooks guide.

Going further

Production checklist

  • Scope tokens correctly. A personal access token is a back-office credential; a customer access token from the SSO flow is the customer's own credential. Never use a PAT on a my-account endpoint or a customer token on a back-office endpoint. Keep them in separate variables in your code so the type confusion can't happen by accident.
  • Audit-log every customer write. The webhook events give you the what; your own application logs should give you the who. Log the staff user and the request ID against every write you make with a PAT, so you can answer "who changed this customer's address last Tuesday?" without going back to the platform.
  • Treat marketing consent as the source of truth. Don't store a "subscribed?" boolean in your own database — store an audit trail of the opt-in events and treat the latest one as authoritative. The marketing_preference_opt_in.updated webhook gives you the granularity you need.
  • Handle GDPR data-subject requests via the platform. For deletion requests, use deleteCustomer to anonymise the record rather than DELETE against your own database. The platform retains the anonymised record for accounting purposes (orders, tax) and zeros out the PII; your own application should follow suit.
  • Don't store card details client-side. The platform never sees raw PANs; nor should you. Capture cards via Stripe Checkout (or whichever processor the site is configured with) and rely on Trybe's tokenised method record as the durable reference.
  • Verify webhook signatures. Customer-domain events carry PII; unverified deliveries could be forged. Compute the SHA-256 HMAC using your configured secret and reject mis-signed deliveries.
  • Respect the brand boundary. A customer_id from one brand cannot be used in operations scoped to another brand. If your integration covers multiple brands, namespace your downstream data accordingly — don't merge customers across brands without the customer's explicit consent and a clear linking record.
  • Surface consent state on every customer-facing form. When asking the customer for information that would be used for marketing — phone number, email, postcode — show the current consent state and let them opt in or out in line. Bury it in a separate "settings" tab at your peril.
  • Retry only reads on 5xx. Customer reads (getCustomer, listLabels) are safe to retry with exponential backoff. Writes aren't — an unconditional retry of updateCustomer after a 502 could overwrite a concurrent change. Re-fetch first and reconcile.