Updating People from Customer.io
Two operations you can run from any Customer.io workflow — add someone to a community, or change their signup code.
You can run two operations against the platform from a Customer.io workflow (or from a Claude / terminal script):
- Add a person to a community.
- Set a person's signup code.
That's it. There are no other operations and nothing else can be changed through this route. Every call is recorded in the audit trail and posted to #ops-webhooks in Slack.
Before You Start
You need a platform access token. Engineering hands these out — they are not self-serve.
- For Customer.io workflows, engineering will set up the token once as a workspace credential. After that, every workflow you build just references it.
- For scripts (Claude, terminal, anything you write yourself), engineering will hand you a separate token. Treat it like any other API key — never paste it into Slack, never commit it to git.
The token only has permission to do these two operations. It cannot read profiles, see investments, or change anything else.
How a Customer.io Webhook Action Is Structured
Every call uses Customer.io's built-in Webhook action. The UI has four parts you fill in, in this order:
- Method — a dropdown. Always set to
POST. - URL — a single text field. Paste the full URL for the operation you want (URLs are listed below).
- Headers — a list of
Name/Valuerows. Click Add header for each row. You always need three headers (see below). - Body — the JSON payload. Click into the body editor and paste the JSON block for the operation.
The three headers are the same for every operation:
| Header name | Value |
|---|---|
Content-Type | application/json |
Authorization | Bearer followed by the workspace token (e.g. Bearer abc123…). Engineering hands you the token once. |
X-CIO-Idempotency-Key | {{ event.delivery_id }} — leave this as Liquid. Customer.io substitutes a unique value per delivery so retries are safe. |
Do not skip X-CIO-Idempotency-Key. Without it, if Customer.io retries the same delivery (which it does on transient failures), the platform has no way to know it's a retry and the operation could run twice. With it, retries are a guaranteed no-op.
The two operations below differ only in their URL and Body. Method and headers are identical.
Operation 1 — Add Someone to a Community
URL:
https://app.letsplaymoney.com/api/v1/ops/people/communities/add
Body:
{
"data": {
"type": "communityMembership",
"attributes": {
"personId": {{ customer.id }},
"communityId": 42
}
}
}
What to fill in:
{{ customer.id }}— leave this as Liquid. For every normal investor profile, Customer.io stores the person's Play Money ID here and substitutes it automatically.42— the numeric ID of the community you want them added to. Open the community in admin and look at the URL:/admin/communities/42→ the ID is42. Send it as a number, not a string (no quotes).
If you don't have the Play Money ID handy, you can send "email" instead of "personId".
A small number of older Customer.io profiles — people who interacted with us only through email preferences before they had a full account — store an email address in {{ customer.id }} instead of a number. If a workflow fails with "personId must be an integer", switch the body to send "email": "{{ customer.email }}" and it will work.
What the response tells you:
"alreadyMember": false— added them."alreadyMember": true— they were already a member. Nothing changed.
Either way, the call succeeded. It is safe to call this for the same person repeatedly — no duplicate memberships, no duplicate audit entries.
Operation 2 — Set Someone's Signup Code
URL:
https://app.letsplaymoney.com/api/v1/ops/people/code
Body:
{
"data": {
"type": "personSignupCode",
"attributes": {
"personId": {{ customer.id }},
"code": "VC-Partners"
}
}
}
Replace "VC-Partners" with the code you want to set.
To clear someone's code (reset it to the platform default), send the word null for the code value:
"code": null
Do not leave code out of the body entirely. A missing field returns an error rather than clearing the value. This is on purpose — if a Customer.io Liquid variable were accidentally empty, we don't want to wipe everyone's signup code silently. To clear, you must say null explicitly.
What the response tells you:
"changed": true— the code was updated."previousCode"shows what it was before."changed": false— the code was already that value. Nothing changed.
Safe to repeat.
When Something Fails
Customer.io will log the HTTP status. Here's what each one means and who fixes it:
| Status | Meaning | Who handles it |
|---|---|---|
| 200 | Success | You're done. |
| 401 | The token is invalid or has been rotated | Tell engineering. |
| 403 | The token doesn't have permission, or the request was blocked | Tell engineering. |
| 404 | No matching person or community | You — double-check the person ID/email and the community ID. |
| 409 | Two different people share the email you sent | You — send the Play Money ID instead. |
| 422 | The body is missing a required field or has a bad value | You — read the error message in the response. |
| 429 | Too many requests in the last minute | Slow down. Customer.io retries on its own. |
| 5xx | The platform is temporarily unhealthy | Tell engineering. |
Every error response includes a plain-English detail field that says exactly what went wrong. Read it first before asking for help.
Good to Know
- One person, one operation, one call. There is no batch endpoint. To add 50 people to a community, send 50 calls — Customer.io workflows handle this naturally because they run per-person anyway.
- Audit trail. Every successful call shows up in the audit trail attributed to "Customer.io" (or "Marketing Ops" if it came from a script). The previous value, new value, and the originating workflow are all recorded.
- Slack. Watch
#ops-webhooksfor a live log of every call. If a workflow you just published is misbehaving, you'll see it there immediately.
Running It from a Script Instead
Customer.io isn't the only way to call these. If you're scripting (Claude, a one-off terminal command, a small Python tool), you use the script token engineering gave you instead of the workspace token. The URL and body are identical to what's above. The only difference: scripts only need the Content-Type and Authorization headers — X-CIO-Idempotency-Key is Customer.io-specific, so skip it (or send your own unique value if you want retry safety).
Last updated today
Built with Documentation.AI