Create a contact
Saves one address in the workspace address book. The address is the identity, so there is no id to choose and none in the response. It is trimmed and lowercased before it is stored, and the contact is saved with `source` set to `manual`. The new contact joins the built-in default audience as it is written, plus anything named in `audienceIds`.
Runs the real call on your workspace.
POST /contacts
Saves one address in the workspace address book. The address is the identity, so there is no id to choose and none in the response. It is trimmed and lowercased before it is stored, and the contact is saved with source set to manual. The new contact joins the built-in default audience as it is written, plus anything named in audienceIds.
Example
Needs contacts:write. Sending audienceIds also needs audiences:write.
curl -X POST "$OE/contacts" -H "$AUTH" -H 'content-type: application/json' -d '{ "email": "[email protected]", "name": "Grace Hopper", "notes": "Met at the compiler workshop", "attributes": { "plan": "pro" }}'{ "object": "contact", "email": "[email protected]", "name": "Grace Hopper", "source": "manual", "notes": "Met at the compiler workshop", "attributes": { "plan": "pro" }, "lastSeenAt": null, "createdAt": "2026-09-18T09:00:00.000Z", "updatedAt": "2026-09-18T09:00:00.000Z", "audiences": [ { "id": "aud_9f2c4b7e1a0d63d84c5f2e7b", "name": "All contacts", "builtin": "default" } ]}An address already in the book is a 409 contact_exists rather than a merge, so a retry cannot quietly overwrite a name somebody edited in the app. Read the row and PATCH it instead.
lastSeenAt stays null until mail goes to the address from the app composer. source and lastSeenAt are the server’s and are not accepted in the body.
An audience id in audienceIds that does not exist in this workspace is a 404 and nothing is written.
attributes holds the contact’s custom fields: up to 50 text values of at most 500 characters, under keys that start with a letter and hold letters, digits and underscores. Automations read one as attributes.<key>, and every contact answer carries them. A field that breaks a rule is a 422 invalid_contact.