Skip to main content
v2 is a new version of the API, served under /api/v2 next to /api/v1. Nothing changes for a request you keep sending to /api/v1. Your API token works on both versions, and you can move one endpoint at a time. This page lists what you need to change when you move a call from v1 to v2. Each endpoint page in the API reference documents the full v2 contract.

Before you start

1

Check your token's scopes

v2 splits some v1 scopes and adds new ones. See Scopes and request any you are missing from Origin One.
2

Treat every id as a string

All v2 id values are strings. See Identifiers.
3

Update your error handling

A missing scope returns 403, not 401, and error code values are new. See Errors.
4

Move each endpoint

Change the path from /api/v1 to /api/v2 and apply the changes for that endpoint below.
5

Move your webhook callbacks

v2 deliveries have a new body and signature. See Migrate webhooks to v2.

Endpoints

Most v1 endpoints have a v2 equivalent at the same path under /api/v2. Two are removed: v2 adds these endpoints:

Scopes

Scope names keep the resource.action convention. These differ between versions: Every other scope keeps its name. See Scopes for the full table.

Errors

A token that lacks the scope an endpoint requires gets 403 Forbidden in v2. v1 returns 401 Unauthorized for the same request. In v2, 401 means only that the bearer token is missing, invalid, or expired. A resource outside your token’s access no longer returns 403. Where v1 returns 403 for a location or package the token isn’t linked to, v2 returns 404 Not Found from a get endpoint and an empty page from a list filter such as locationId. Every error a v2 endpoint returns, including 401, 403, and 404, has the same body as a validation error:
What changes in the body:
  • code values are new. v1 returns validation constraint names such as isNotEmpty and isString. v2 uses its own vocabulary, such as required, invalidType, invalidValue, and tooSmall. Don’t map v1 codes to v2 codes. Rely on statusCode, source.pointer, and detail instead.
  • source can be omitted. It is present only on 400 errors that a specific request field caused.
  • The top-level meta field is removed.
A request that fails before it reaches an endpoint, such as one with a malformed JSON body or an unknown path, still returns the v1 error body. Create an order can also return 409 Conflict when a package name matches more than one package.

Identifiers

Every v2 id is a string. In v1, company, location, package, and clinic ids are JSON numbers. The value stays the same: a location with "id": 456 in v1 has "id": "456" in v2. Order and service ids were already strings and don’t change. The change applies wherever an id appears: response bodies, path parameters, query filters such as companyId and locationId, and request bodies such as locationId in Search clinics. Store ids as opaque strings. See Identifiers.

Response conventions

v2 applies the same rules to every resource. These change what v1 returns:
  • Every resource carries object. Nested resources carry it too, including address. See Resource type.
  • Every resource you can fetch carries url, its canonical path. Follow it instead of building paths from id. See Resource URLs.
  • Related resources are nested references, not flat fields. A location returns company: { object, id, name, url } instead of companyId and companyName.
  • Empty values are null. v1 can return an empty string for an absent address field. v2 returns null, and a field your token can’t see is omitted. See Null versus omitted.
  • Addresses have one shape: object, line1, line2, city, state, postalCode, and country. Each value can be null.
  • Response enums are open. Values such as an order’s status can gain new members. Handle values you don’t recognize. See Forward compatibility.
Pagination is unchanged: list endpoints take page and limit and return the { "data", "total", "page", "limit" } envelope. See Pagination.

Orders

Create an order

Create an order takes a reorganized body. Here is the same order in each version:
v2 ignores request fields it doesn’t define, so a v1 field name left in a v2 request is dropped without an error. A required field then fails validation, but an optional one, such as clinicSettings.selectionMode, silently falls back to its default. Rename every field in the table below.
The callback, orderMatching, and the remaining settings fields keep their names. settings.observedCollection must be true for a return-to-duty or follow-up test with a DOT urine service: v1 skips that check when the field is omitted, and v2 rejects the order. Registering a callback through v2 changes what its deliveries look like: see Migrate webhooks to v2.

Order responses

Create an order, Get an order, and List orders return the same v2 order. Cancel an order returns it too, without the participant’s identifiers. The v2 order also adds the order’s context, so you no longer need a second call for it: url, agency, clinic, company, location, jobCategory, packages, participant, testReason, otherTestReason, createdAt, updatedAt, statusUpdatedAt, and expiresAt. Each service adds id, isDot, attributes, lab, specimenId, trackingNumber, courierServiceName, and screenHistory, and each statusHistory entry adds groupType. participant.identifiers is present only for a token with the order.participant.read scope. services[].statusUpdatedAt can be null. See Order lifecycle for statuses and isFinal.

Searching orders

POST /api/v1/orders/search is replaced by GET /api/v2/orders, which takes query-string filters instead of a filter body: v2 can’t filter orders by participant details (name, email, phone, date of birth, SSN, or driver license) or by updatedAt. To follow status changes, register a callback when you create the order instead of polling. The list has no implicit date window: it returns every order your token can see unless createdFrom or createdTo narrow it.

Cancel an order

Cancel an order keeps its path, its body, and its 422 cases. It returns the v2 order without the participant’s identifiers: get the order for them.

Clinics

Search clinics

Search clinics returns only active clinics owned by your tenant. Send coordinates or address, not both. An address requires state and postalCode. In the response, clinicType and vendors are removed and isActive is added.

Clinic responses

List clinics, Get a clinic, and search results list a clinic’s services as references (object, id, name, type, url). services[].isDot and services[].attributes are removed from the clinic; fetch the service’s url for them.

Locations, companies, packages, and services

Webhooks

A callback receives the deliveries of the version it was registered through. v2 deliveries carry an event instead of the order, and sign it with a timestamp. See Migrate webhooks to v2 for the switch-over steps.