Migrating to V2
Migrating to V2
The Doppel V2 API is a drop-in replacement for V1 with a new authentication model. Every V1 endpoint is available at the same path under /v2/ and accepts the same request bodies, query parameters, and response shapes.
In other words: if your integration works against /v1/..., the migration to /v2/... is fundamentally an authentication change.
What changed
| V1 | V2 | |
|---|---|---|
| Auth headers | x-api-key (organization-wide), or x-api-key + x-user-api-key (user-specific). See Authentication. | Authorization: Bearer <access_token> |
| Token issuance | Static keys: organization-wide keys are issued by your Doppel sales representative; user-specific keys are self-service from Doppel Vision API settings. See Authentication. | OAuth 2.0 client credentials at POST /oauth/token; access_token expires after expires_in seconds (24 hours at the time of writing) |
| Org scope header | x-organization-code: <ORG> (optional; per the V1 OpenAPI spec, required only when your user belongs to multiple organizations) | Not part of the V2 contract — organization is derived from the org_id claim in the JWT |
| Path prefix | /v1/<resource> | /v2/<resource> |
| Request/response bodies | — | Unchanged for all non-deprecated endpoints |
If your code does not use deprecated endpoints (see below), the only required code changes are: (1) exchange your client credentials for a token, (2) replace the V1 auth headers with Authorization: Bearer ..., (3) drop x-organization-code if you were sending one, (4) change /v1/ to /v2/ in your request URLs.
Deprecated endpoints
The following V1 endpoints are documented as deprecated: true in the V2 OpenAPI spec. They still work in V2, but new integrations should not depend on them.
POST /v2/report— usePOST /v2/alertinsteadGET /v2/report— useGET /v2/alertinsteadPUT /v2/report— usePUT /v2/alertinsteadGET /v2/reports— useGET /v2/alertsinstead
The report resource and the alert resource refer to the same underlying object. The migration is a rename: replace report with alert (and reports with alerts) in the path. Request bodies and response shapes are otherwise identical.
Step 1: Get an OAuth access token
Your Client ID and Client Secret are issued by your Doppel representative and replace the V1 x-api-key and x-user-api-key pair. Exchange them for an access token via POST /oauth/token. See the V2 Authentication guide on the V2 docs site for the full token-exchange flow (including refresh handling).
Step 2: Replace your auth headers
V1 (before)
The example below shows the user-specific V1 mode (x-api-key + x-user-api-key) with an explicit x-organization-code. If you were authenticating with x-api-key alone (organization-wide), simply drop the other two headers from the V1 request.
cURL
curl --request GET \
--url "https://api.doppel.com/v1/alerts?queue_state=needs_confirmation" \
--header "x-api-key: <YOUR_ORG_API_KEY>" \
--header "x-user-api-key: <YOUR_USER_API_KEY>" \
--header "x-organization-code: ACM"Python
import requests
response = requests.get(
"https://api.doppel.com/v1/alerts",
headers={
"x-api-key": "<YOUR_ORG_API_KEY>",
"x-user-api-key": "<YOUR_USER_API_KEY>",
"x-organization-code": "ACM",
},
params={"queue_state": "needs_confirmation"},
)Node.js
const url = new URL("https://api.doppel.com/v1/alerts");
url.searchParams.set("queue_state", "needs_confirmation");
const response = await fetch(url, {
headers: {
"x-api-key": "<YOUR_ORG_API_KEY>",
"x-user-api-key": "<YOUR_USER_API_KEY>",
"x-organization-code": "ACM",
},
});V2 (after)
cURL
curl --request GET \
--url "https://api.doppel.com/v2/alerts?queue_state=needs_confirmation" \
--header "Authorization: Bearer <YOUR_ACCESS_TOKEN>"Python
import requests
response = requests.get(
"https://api.doppel.com/v2/alerts",
headers={"Authorization": f"Bearer {access_token}"},
params={"queue_state": "needs_confirmation"},
)Node.js
const url = new URL("https://api.doppel.com/v2/alerts");
url.searchParams.set("queue_state", "needs_confirmation");
const response = await fetch(url, {
headers: { Authorization: `Bearer ${access_token}` },
});The query string, response shape, and pagination behavior are identical to V1 — only the auth headers and path prefix change.
Step 3: Handle token expiration
V2 access tokens expire after the expires_in window returned by POST /oauth/token (24 hours at the time of writing). Your client must refresh on 401 Unauthorized, or proactively before expiration. A typical pattern:
import time
import requests
_token = None
_token_expires_at = 0
def get_token() -> str:
global _token, _token_expires_at
if _token and time.time() < _token_expires_at - 60:
return _token
response = requests.post(
"https://api.doppel.com/oauth/token",
json={
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"audience": "doppel-external",
"grant_type": "client_credentials",
},
)
response.raise_for_status()
data = response.json()
_token = data["access_token"]
_token_expires_at = time.time() + data["expires_in"]
return _tokenMigration checklist
- Obtain Client ID and Client Secret from your Doppel representative.
- Implement the OAuth token-exchange flow (see the V2 Authentication guide).
- Replace
x-api-key(and, if you were sending it,x-user-api-key) withAuthorization: Bearer <token>. - Drop the
x-organization-codeheader if you were sending one — V2 derives the organization from the token. - Change
/v1/to/v2/in every request URL. - Add token-refresh handling on
401 Unauthorized. - If you call
/v1/report*or/v1/reports, switch to/v2/alert*or/v2/alerts. - Run your existing integration tests against
/v2/to confirm response shapes are unchanged.
Need help?
If you hit a difference between V1 and V2 that is not covered here, contact your Doppel representative — please include the request URL, the V1 vs V2 response, and any HTTP status codes you observed.
