Issue a contact's browser visitor id
Returns the contact's visitor id — a random, opaque identifier you set as a first-party cookie so that SendOps can recognise the person when they visit your website.
Call this from your own server, at login and at signup. Never from
a browser: the response is issued to an API key, and the cookie has to
be set by a Set-Cookie header from your own domain.
Find-or-create and safe to repeat. A contact has exactly one active
visitor id; the first call mints it and returns result: "created",
every later call returns the same id with result: "existing".
Re-calling on every login is the intended usage — it is what keeps the
cookie's 400-day window rolling.
Setting the cookie
Set-Cookie: so_vid=vis_…; Max-Age=34560000; Domain=.example.com; Path=/; Secure; SameSite=Lax
Four things about that line are load-bearing:
- Not
HttpOnly. The SendOps snippet reads the cookie with JavaScript. Marking itHttpOnlymakes the whole feature silently do nothing. Domainis your registrable domain (.example.com), not the host your app runs on. That is what lets the snippet onwww.example.com,docs.example.comandblog.example.comsee it. Scoping it toapp.example.commeans only your app can read it, which is the one place you do not need it.- Re-set it on every login.
Max-Agerestarts from eachSet-Cookie, so a returning customer's window never expires. - Never write it from JavaScript. Safari's Intelligent Tracking Prevention caps a script-written cookie at 7 days, which defeats the point. Set it from your server, and let the snippet only read it.
Clear the cookie on logout (Max-Age=0, same Domain and Path), so a
shared browser does not attribute the next person's visits to the one
who left.
Identity resolution
Supply email, external_id, or both. The rules match
POST /v1/activities:
- An
emailwe have not seen creates a contact for it. - An
external_idwe have not seen, with noemailalongside it, is refused with422 unknown_external_id— an external id is your own key, and one we have never seen is more likely a typo than a person. - An
emailwhose contact already carries a differentexternal_idis refused with422 external_id_conflict.
Archiving a contact revokes its visitor id: any cookie still in a browser stops resolving, and the beacon carrying it is dropped without being recorded.
Supports the Idempotency-Key header: a duplicate key within 24 h
replays the original response.
Request body
Content type: application/json
email string<email> optional The contact's email address. An address we have not seen creates a
contact for it, exactly as POST /v1/activities does.
external_id string optional Your own stable identifier for the person. On its own, one we have
never seen returns 422 unknown_external_id rather than creating a
contact — pair it with email if you want it created.
Responses
Errors follow the RFC 7807 problem format — see the error reference.
contact_id string<uuid> required The contact the visitor id belongs to.
visitor_id string required The value to set as the so_vid cookie. Opaque, random, and
derived from nothing about the person — treat it as a credential
and do not log it.
Constraints: pattern ^vis_[0-9a-hjkmnp-tv-z]{26}$
result string enum required created on the call that minted the id, existing on every later
call for the same contact. The id itself is identical either way.
One of: created, existing
email nor
external_id. The code is validation_failed.
Declared here rather than as a shared response because it is the
only 400 on this API: everywhere else a validation fault is
422, and this one is the narrower "there is nothing here to
process" case.
application/problem+json unknown_external_id — no contact carries that external_id and
no email was supplied. Supply an email alongside it, or create
the contact first.
- external_id_conflict — that email belongs to a contact under
a different external_id. Permanent until reconciled; retrying
changes nothing.
- validation_failed — the body was not a JSON object.
application/problem+json code is internal_error. The
request_id field can be quoted to SendOps support to investigate.
application/problem+json