# Migration Guide



This guide walks through migrating an existing v1 integration to the v2 API. v2
is not backwards compatible, but most changes are mechanical — a new base path,
camelCase fields, and a consistent response envelope — with a few flows (user
creation, authentication) split into clearer, dedicated endpoints.

## Deprecation timeline [#deprecation-timeline]

The v1 API was deprecated on **July 24, 2026** and sunset on
**August 21, 2026**. It is now shut down: every v1 endpoint returns
`410 Gone` with a `Link` header pointing to this guide. All integrations must
be on v2.

| Date                | Milestone                                                                                                                    |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| **July 24, 2026**   | **Deprecated.** v2 becomes the required API for all integrations. v1 keeps working but receives no further updates or fixes. |
| **August 21, 2026** | **Sunset.** v1 shuts down and stops accepting requests. All integrations must be on v2.                                      |

<Callout type="error" title="v1 is sunset">
  v1 no longer accepts requests. The [v1 documentation](/docs) is kept for
  reference only, so you can map your existing integration onto v2 — do not
  build against it. If your integration was granted a sunset extension and you
  are still calling v1, [reach out](/v2/support) to coordinate your cutover.
</Callout>

## What changed at a glance [#what-changed-at-a-glance]

* **Versioned base path** — every endpoint now lives under `/v2`.
* **Consistent response envelope** — successful responses are wrapped in
  `{ "success": true, "data": ... }`; errors return
  `{ "success": false, "errors": [...] }`.
* **camelCase everywhere** — request and response fields use camelCase
  (`first_name` → `firstName`, `address1` → `addressLine1`, `postal` →
  `postalCode`).
* **RESTful, resource-based endpoints** — verb-style endpoints
  (`/generateApiLink`, `/loginCodeSend`) are replaced with resources
  (`/users`, `/auth/login-codes`).
* **Separated authentication** — integrator requests use `x-api-key`; requests
  made on behalf of an end user now use a short-lived JWT
  (`Authorization: Bearer <token>`).

## Base URL [#base-url]

```diff
- https://api.paytrie.com/<endpoint>
+ https://api.paytrie.com/v2/<endpoint>
```

## Response envelope [#response-envelope]

v1 returned the payload (or an ad-hoc `{ status, message }` object) directly. v2
always wraps the payload so success and errors are predictable.

```json title="v1 response"
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "email": "john.doe@example.com"
}
```

```json title="v2 response"
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "john.doe@example.com"
  }
}
```

```json title="v2 error"
{
  "success": false,
  "errors": [{ "field": "email", "message": "Invalid email" }]
}
```

<Callout type="info">
  Update your client to read the payload from `response.data` and to check
  `response.success` (or the HTTP status) for errors.
</Callout>

## Endpoint mapping [#endpoint-mapping]

| v1 endpoint                    | v2 endpoint                                             |
| ------------------------------ | ------------------------------------------------------- |
| `POST /loginCodeSend`          | `POST /v2/auth/login-codes`                             |
| `POST /loginCodeVerify`        | `POST /v2/auth/login-codes/verify`                      |
| `POST /generateApiLink`        | `POST /v2/users` + `GET /v2/users/{userId}/kyc-url`     |
| `GET /apiFindUser`             | `GET /v2/users?email={email}`                           |
| `POST /sumsubImportUser`       | `PUT /v2/users/{userId}/kyc`                            |
| `GET /priceQuote`              | `GET /v2/quotes`                                        |
| `GET /transaction`             | `GET /v2/transactions` + `GET /v2/transactions/{txId}`  |
| `POST /transaction`            | `POST /v2/transactions`                                 |
| `GET, PATCH /webhooks`         | `GET, PUT, DELETE /v2/webhook-configurations`           |
| `POST /webhook-signing-secret` | `POST /v2/webhook-configurations/signing-secret/rotate` |

New in v2 with no v1 equivalent: `GET /v2/limits`,
`GET /v2/users/{userId}/requirements`, `PATCH /v2/users/{userId}`, and
`PUT /v2/transactions/{txId}/cancel`.

## Migration examples [#migration-examples]

### Request a login code [#request-a-login-code]

```bash title="v1"
curl -X POST "https://api.paytrie.com/loginCodeSend" \
  -H "x-api-key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{ "email": "user@example.com" }'
```

```bash title="v2"
curl -X POST "https://api.paytrie.com/v2/auth/login-codes" \
  -H "x-api-key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{ "email": "user@example.com" }'
```

Then exchange the code for a JWT:

```bash title="v2"
curl -X POST "https://api.paytrie.com/v2/auth/login-codes/verify" \
  -H "x-api-key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{ "email": "user@example.com", "code": "1234" }'
```

### Create a user [#create-a-user]

User creation and KYC are now two steps: create the user, then request a KYC URL.

```bash title="v1"
curl -X POST "https://api.paytrie.com/generateApiLink" \
  -H "x-api-key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "John",
    "last_name": "Doe",
    "email": "john.doe@example.com",
    "phone": "4165551234",
    "dob": "1990-01-15",
    "address1": "123 Main Street",
    "address2": "Suite 100",
    "city": "Toronto",
    "province": "on",
    "postal": "M5V1A1",
    "occupation": "Software Engineer",
    "pep": false,
    "tpd": false
  }'
```

```bash title="v2"
curl -X POST "https://api.paytrie.com/v2/users" \
  -H "x-api-key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "john.doe@example.com",
    "firstName": "John",
    "lastName": "Doe",
    "dob": "1990-01-15",
    "phone": "4165551234",
    "addressLine1": "123 Main Street",
    "addressLine2": "Suite 100",
    "city": "Toronto",
    "province": "on",
    "postalCode": "M5V1A1",
    "occupation": "Software Engineer",
    "pep": false,
    "tpd": false
  }'
```

Then generate the KYC link with the returned `id`:

```bash title="v2"
curl -X GET "https://api.paytrie.com/v2/users/{userId}/kyc-url" \
  -H "x-api-key: your-api-key"
```

See [Customer Onboarding](/v2/customer-onboarding) for the full onboarding flow.

### Fetch a price quote [#fetch-a-price-quote]

```bash title="v1"
curl -X GET "https://api.paytrie.com/priceQuote?leftSideLabel=CAD&leftSideValue=100&rightSideLabel=USDC-ETH" \
  -H "x-api-key: your-api-key"
```

```bash title="v2"
curl -X GET "https://api.paytrie.com/v2/quotes?leftSideLabel=CAD&leftSideValue=100&rightSideLabel=USDC-ETH" \
  -H "x-api-key: your-api-key"
```

The v2 quote response is enveloped and adds a `quoteId` (pass it to
`POST /v2/transactions` to lock in the rate) plus a fee `breakdown`.

### Create a transaction [#create-a-transaction]

The v2 body drops the client-supplied `ethCost` and `gasId` fields — pricing is
derived server-side from the `quoteId`.

```bash title="v1"
curl -X POST "https://api.paytrie.com/transaction" \
  -H "x-api-key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "leftSideLabel": "CAD",
    "leftSideValue": 1000,
    "rightSideLabel": "USDC-ETH",
    "wallet": "0x05a238198541d076B4fc74254Cd426A8C8e84D32",
    "ethCost": 3440.64,
    "gasId": 2,
    "quoteId": 1
  }'
```

```bash title="v2"
curl -X POST "https://api.paytrie.com/v2/transactions" \
  -H "x-api-key: your-api-key" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "leftSideLabel": "CAD",
    "leftSideValue": 1000,
    "rightSideLabel": "USDC-ETH",
    "wallet": "0x05a238198541d076B4fc74254Cd426A8C8e84D32",
    "quoteId": 1
  }'
```

### Migrate webhooks [#migrate-webhooks]

Registering a v2 webhook does **not** disable v1 webhooks, so if you had v1 webhooks configured they keep firing alongside v2 until you turn them off:

1. Register your v2 webhook URL with `PUT /v2/webhook-configurations`.
2. Confirm v2 events are arriving at your endpoint.
3. Once you're confident, turn off v1 yourself.

To turn off v1 webhooks, use the [`PATCH /webhooks`](/docs/webhooks#update-webhook-configuration) call — it remains available for this purpose after the sunset — and set every enabled flag to `false`:

```bash title="Disable v1 webhooks"
curl -X PATCH "https://api.paytrie.com/webhooks" \
  -H "x-api-key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "verifiedEmailEnabled": false,
    "transactionInitiatedEnabled": false,
    "transactionCompleteEnabled": false,
    "transactionStatusUpdateEnabled": false
  }'
```

See [Webhooks](/v2/webhooks) for the full v2 webhook setup, payload schemas, and signature verification.

## Next steps [#next-steps]

<Card title="v2 API Reference" href="/v2/api-reference" icon="arrow-right-left">
  Browse the full list of v2 endpoints, parameters, and response schemas
</Card>

<Card title="Need help migrating?" href="/v2/support" icon="life-buoy">
  Reach out and we'll help you plan the migration
</Card>
