Skip to content

Enrol a customer through the join gate

POST /customers/{customer_id}/enroll
Scopecustomers:write
Customer session tokenAccepted with the write scope, for the token’s own customer
Rate limit classwrite (limits)
Idempotency keyRequired (idempotency)
MCP toolcharm_enroll_customer

The explicit join for storefronts without the widget (headless shops). Same path as the widget’s Join button: first join wins, the signup bonus is credited at the join moment, locale is remembered for e-mails. Idempotent — an already-joined customer answers changed: false. Refused with join_gate_enabled: false on programs without a join gate, where customers are enrolled automatically when their Shopify account is created or their first order lands. The customer must exist in Shopify.

NameInTypeDescription
customer_idpathstringShopify customer GID (percent-encoded, e.g. gid%3A%2F%2Fshopify%2FCustomer%2F123) or the numeric customer id.
FieldTypeDescription
localestringBCP 47 tag for the customer’s e-mails, e.g. it. (optional)
marketing_consentbooleanTrue records e-mail marketing consent in Shopify when the join gate asks for it. (optional)

Returns CustomerWriteResult.

FieldTypeDescription
changedboolean
customerCustomer or CustomerState
previous_tier_idstring or null(optional, nullable)
bonus_pointsnumber(optional)
{
"changed": true,
"customer": {
"id": "gid://shopify/Customer/7712345",
"legacy_id": 7712345,
"email": "shopper@example.com",
"first_name": "Jane",
"last_name": "Doe",
"points_balance": 120,
"points_earned": 120,
"points_redeemed": 120,
"pending_points": 120,
"tier": null,
"next_tier": null,
"rewards": [
null
],
"history": [
null
],
"program": {
"enabled": null,
"program_name": null,
"points_name": null,
"points_expiry": null,
"reward_expiry": null,
"eligibility": null,
"referral": null,
"memberships_enabled": null,
"tiers_enabled": null,
"earning_rules": null,
"redemption_rules": null,
"revision": null,
"updated_at": null
},
"referral": null,
"referral_code": "JANE10",
"excluded_from_program": false,
"enrolled_at": "2026-10-01T09:30:00.000Z",
"enrolled_via": "Embedded Widget",
"birthday": null,
"orders_count": 1,
"lifetime_spend": 1,
"last_activity_at": "2026-10-01T09:30:00.000Z",
"updated_at": "2026-10-01T09:30:00.000Z"
},
"previous_tier_id": "silver",
"bonus_points": 120
}
Terminal window
curl -X POST "https://charm.appfleece.app/api/v1/customers/7712345/enroll" \
-H "Authorization: Bearer chrm_live_..." \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"locale":"locale","marketing_consent":false}'

unauthorized · invalid_token · token_expired · insufficient_scope · access_required · not_found · validation_error · rate_limited · idempotency_conflict

Every error body carries code, message, request_id and a doc_url pointing at the matching entry in the error catalog.