Find a person
Use GET /users. It answers every lookup, with pagination and a total. Order the whole list with sortBy (createdAt, updatedAt, email or name; default updatedAt) and sortOrder (asc or desc; default desc); pages keep a stable order, so no row moves between them.
# by your own identifier
curl "https://api-eu1.joryio.com/api/users?externalId=user_123" \
-H "Authorization: Bearer jry_live_your_api_key"
# by email
-H "Authorization: Bearer jry_live_your_api_key"
# partial - anything starting with "jane", plus phone and id matching
curl "https://api-eu1.joryio.com/api/users?q=jane" \
-H "Authorization: Bearer jry_live_your_api_key"
Which parameter
| You have | Use | Matching |
|---|---|---|
| Your own id | ?externalId= | exact |
| An email address | ?email= | exact, case-insensitive |
| Joryio's 24-hex id | ?userId= | exact |
| Part of something - a name, the start of an email, a phone | ?q= | prefix on email, externalId, first and last name; digits anywhere in the phone; whole-value on a 24-hex id or a UUID anonymousId |
Exact parameters combine (?email=…&externalId=… requires both). q is
ignored when an exact parameter is present - "email equals X" and "any field
starts with Y" cannot both narrow one request, and quietly combining them would
answer a question you did not ask.
Nothing found
An empty page, not a 404:
{ "data": [], "pagination": { "total": 0, "limit": 50, "offset": 0, "hasMore": false } }
A userId that is not 24-hex returns the same empty page rather than an error -
a malformed id is a lookup that matches nobody, not a broken request.
A parameter we do not recognise
If a parameter looks like a misspelling of a real one, or if none of your parameters are recognised, the request is refused:
{ "statusCode": 400,
"message": "Unknown query parameter 'externalid'. Did you mean 'externalId'? Valid parameters: limit, offset, email, externalId, userId, q, bots, sortBy, sortOrder" }
This used to be a silent 200 with the full unfiltered page, which is how a typo cost an integrator a day - a filter that does nothing looks exactly like a filter that found everything.
Parameters that arrive without you choosing to send them are still accepted and
ignored: _ and _* (jQuery's cache-buster), utm_*, gclid, fbclid,
msclkid, and t / v / cb. So [email protected]&_=1724500000 works, and
so does ?_=1724500000 on its own.
An unfamiliar parameter that resembles nothing of ours - a gateway's correlation id, say - is also accepted, as long as at least one parameter we understand is present. The rule is not "reject anything unknown", it is "refuse a request that could not have done what you meant".
The other two routes
Both still work. Neither does anything GET /users cannot.
| Route | Still useful when |
|---|---|
GET /users/by-user-id/:externalId | you want the record itself, not a page containing it - it returns the user object directly |
GET /users/search?query= | you want the older shape: a bare array, capped, no total |
GET /users/:userId (Joryio's internal id) is the canonical single-record
route and is not going anywhere - it is what id in every response refers to.
Anonymous contacts
Someone tracked before they identified has no email, no phone and no name. The
only handle you have is the anonymousId the SDK generated, and ?q= matches
it when the term is UUID-shaped:
curl "https://api-eu1.joryio.com/api/users?q=3f9d2c1e-7a54-4b2e-9c1d-8e6f5a4b3c2d" \
-H "Authorization: Bearer jry_live_your_api_key"
To attach that session's history to a known person, see
POST /users/identify.