> ## Documentation Index
> Fetch the complete documentation index at: https://api-portal.etoro.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Partner integration flow

> Onboard users with their eToro credentials, issue non-interactive tokens, associate accounts, and report events back to eToro.

## Purpose and scope

This integration enables a partner platform to authenticate end users against their existing eToro credentials, and — where eToro has granted the corresponding approval — to reuse identity and verification data that eToro already holds for those users.

Two outcomes follow from it. First, the partner does not operate a separate credential store for users who already hold an eToro account: authentication is delegated to eToro, and the partner receives a verified assertion of the user's identity. Second, onboarding data the user has already supplied to eToro need not be requested again, which shortens the partner's registration journey and reduces the volume of data the partner is required to collect, validate, and retain.

### Capability tiers

The integration is delivered in three tiers. They are incremental and independently provisioned: a partner may adopt the first alone, and each subsequent tier requires additional authorization from eToro.

| Tier               | Provides                                                                                                                                                                                                                                                                      | Requires                                                                                  |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| **Authentication** | A verified assertion that the end user holds a specific eToro account, together with a stable pseudonymous identifier (the `sub` claim) by which the partner recognizes that user on subsequent sign-ins. Sufficient on its own to implement sign-in and account association. | A registered application                                                                  |
| **Authorization**  | The ability to invoke eToro API operations on the user's behalf, limited to the scopes the user consented to at sign-in. See [Permitted operations and scopes](#permitted-operations-and-scopes).                                                                             | Scopes granted to the client and consented to by the user                                 |
| **Data reuse**     | Access to identity and verification data eToro already holds for the user — KYC responses, verification status, and outstanding requirements — enabling the partner to pre-populate its own onboarding rather than re-collecting the same information.                        | An explicit data-sharing approval for the integration, in addition to the relevant scopes |

<Note>
  The first tier requires nothing beyond application registration. Data reuse is subject to a separate approval by eToro and is not self-service — see [Data sharing and PII](#data-sharing-and-pii) for the applicable constraints.
</Note>

Where all three tiers are in place, an existing eToro user can complete registration on the partner platform in a single journey, with the two accounts associated by an eToro-issued token rather than by correlation of personal attributes.

## Integration flow

The partner journey comprises five stages:

1. The user authenticates with their eToro account (OAuth 2.0 authorization code flow with PKCE).
2. The partner backend exchanges the authorization code for an access token, a refresh token, and an **ID token**.
3. The partner backend validates the ID token against eToro's JWKS and extracts the user's identifier from it.
4. The partner backend issues a **non-interactive token (NIT)**, enabling continued API access when the user is not present.
5. The partner backend associates its own user record with the eToro account, keyed on the token identifier.

**Stages 1 to 3 are not partner-specific** — they are the standard OpenID Connect sign-in flow, documented once in [SSO with OAuth 2.0](/core/getting-started/sso-oauth), which covers application registration, the discovery document, the authorization request, the code exchange, and ID token validation. Complete that flow first; this page covers everything that follows it.

All endpoints referenced below are production endpoints. The authorization server is `https://www.etoro.com`; the API is `https://public-api.etoro.com`.

<Card title="SSO with OAuth 2.0" icon="right-to-bracket" href="/core/getting-started/sso-oauth">
  Stages 1 to 3: registration, discovery, PKCE authorization, code exchange, and ID token validation.
</Card>

## Issue a non-interactive token

The access token obtained in stage 2 expires within approximately one hour and is bound to the user's interactive session. For operations the partner backend performs while the user is not present — portfolio synchronization, order placement, reconciliation, event reporting — a **non-interactive token (NIT)** is required.

A non-interactive token is a long-lived, scope-limited user credential. It is presented in the `x-user-key` header together with the application's `x-api-key`:

```bash theme={null}
curl -X GET "https://public-api.etoro.com/api/v1/watchlists" \
  -H "x-request-id: <UUID>" \
  -H "x-api-key: <YOUR_API_KEY>" \
  -H "x-user-key: <NIT>"
```

<Warning>
  The `x-api-key` + `x-user-key` pair and `Authorization: Bearer` are **mutually exclusive** authentication methods. A request presenting both is rejected.
</Warning>

The token is issued using the user's access token from stage 2. Retrieve the assignable scopes first, then create the token:

<CodeGroup>
  ```bash cURL theme={null}
  # 1. Which scopes may this token carry?
  curl -X GET "https://public-api.etoro.com/api/v1/sub-accounts/etoro-trading/user-tokens/scopes" \
    -H "x-request-id: <UUID>" \
    -H "Authorization: Bearer <ACCESS_TOKEN>"

  # 2. Issue the non-interactive token for the user's sub-account
  curl -X POST "https://public-api.etoro.com/api/v1/sub-accounts/etoro-trading/user-tokens" \
    -H "x-request-id: <UUID>" \
    -H "x-sub-account-id: <SUB_ACCOUNT_ID>" \
    -H "Authorization: Bearer <ACCESS_TOKEN>" \
    -H "Content-Type: application/json" \
    -d '{
          "userTokenName": "myapp-prod",
          "scopeNames": [
            "etoro-public:trade.real:read",
            "etoro-public:trade.real:write"
          ],
          "ipsWhitelist": ["203.0.113.10"],
          "expiresAt": "2026-12-31T23:59:59Z"
        }'
  ```

  ```json Response 201 theme={null}
  {
    "userTokenId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "userToken": "<NIT — RETURNED ONLY ONCE>",
    "userTokenName": "myapp-prod",
    "clientId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "ipsWhitelist": ["203.0.113.10"],
    "scopes": [{ "scopeName": "etoro-public:trade.real:read" }],
    "expiresAt": "2026-12-31T23:59:59Z",
    "createdAt": "2026-06-06T10:15:00Z"
  }
  ```
</CodeGroup>

The `userToken` value is returned **only in this response**. It must be persisted to a secret store on receipt; no endpoint returns it subsequently.

| Operation                                    | Endpoint                                                              |
| -------------------------------------------- | --------------------------------------------------------------------- |
| List a user's tokens (secret never included) | `GET /api/v1/sub-accounts/etoro-trading/user-tokens`                  |
| Allowed scopes for these tokens              | `GET /api/v1/sub-accounts/etoro-trading/user-tokens/scopes`           |
| Issue a token                                | `POST /api/v1/sub-accounts/etoro-trading/user-tokens`                 |
| Change scopes, IP allow-list, or expiry      | `PATCH /api/v1/sub-accounts/etoro-trading/user-tokens/{userTokenId}`  |
| Revoke a token (idempotent)                  | `DELETE /api/v1/sub-accounts/etoro-trading/user-tokens/{userTokenId}` |

<Note>
  The authorization server additionally advertises the RFC 8693 token-exchange grant, `urn:ietf:params:oauth:grant-type:token-exchange`. Eligibility for this grant is determined per partner at registration. Confirm with your eToro contact which non-interactive token provisioning path applies to your integration before implementing against it.
</Note>

`expiresAt` and `ipsWhitelist` should be set on every non-interactive token issued. A long-lived user credential with neither an expiry nor an address restriction represents the highest-value target in the integration, and both fields remain modifiable afterwards via `PATCH`. Rotate the token on a schedule with `POST`-then-`DELETE`, and revoke it immediately on user disconnect, suspected compromise, or offboarding.

## Associate the accounts

The association is a mapping maintained by the partner backend between its own user record and the eToro subject. The eToro side of that mapping is retrieved with `GET /api/v1/sub-accounts/me/accounts`, using either the access token or the non-interactive token:

```bash theme={null}
curl -X GET "https://public-api.etoro.com/api/v1/sub-accounts/me/accounts" \
  -H "x-request-id: <UUID>" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"
```

```json theme={null}
{
  "accounts": [
    {
      "subAccountId": "ZXRvcm8tdHJhZGluZw",
      "status": "Approved",
      "providerName": "etoro-trading",
      "accountId": "123456789",
      "accountType": "Trading",
      "accountVisibility": "Visible",
      "externalUserId": "123456789",
      "additionalData": { "gcid": 123456789, "username": "my-trading-account" }
    }
  ]
}
```

* `subAccountId` — the account handle presented in `x-sub-account-id` on sub-account operations, including issuance of the non-interactive token above.
* `externalUserId` — the identifier mapping an eToro account to the partner's own user record, making the association explicit on both sides.
* `status` — `Pending`, `MissingKyc`, `InvalidKyc`, `RejectedByThirdParty`, or `Approved`. Only an `Approved` account should be acted upon. The KYC-related states indicate an incomplete onboarding journey rather than a failure, and should be handled accordingly.

Where the user does not yet hold an account with the provider, one is created with `POST /api/v1/sub-accounts/me/accounts`:

```bash theme={null}
curl -X POST "https://public-api.etoro.com/api/v1/sub-accounts/me/accounts" \
  -H "x-request-id: <UUID>" \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
        "providerName": "etoro-trading",
        "additionalParams": { "username": "my-trading-account" }
      }'
```

Persist the association as `(partner user id, sub claim, subAccountId, externalUserId)` and resolve it from the `sub` claim on each subsequent sign-in. Because `sub` is pairwise and stable for a given client, a returning user resolves to the same record without the partner holding an email address or a name.

<Warning>
  Accounts must never be associated by matching an email address, phone number, or name across systems. Such attributes are not available by default, and matching on user-supplied values constitutes an account-takeover vector: any party able to register that value on the partner platform would inherit the corresponding eToro account. The association must be established solely by a token issued by eToro.
</Warning>

## Permitted operations and scopes

Once a validated token has been obtained for an associated user, the scopes carried by that token determine which operations the partner may execute. The operations available are those published in the [Partners API reference](/partners-api-reference); there is no separate partner endpoint list, and the reference is the authoritative contract.

The same rule applies to either credential type:

* An **access token** or a **NIT** carries a set of scopes, fixed at consent time or at issuance time.
* Every operation in the reference lists the scopes that grant it. A token needs **one** of the listed alternatives, not all of them.
* A call whose token lacks any of an operation's scopes is rejected. Nothing is granted implicitly by being an approved partner.

A token consented to the KYC and money scopes is therefore sufficient to execute the complete user journey exposed by the Partners API on the user's behalf:

| Scope family                                                                                               | What it unlocks in the reference                                                                                               |
| ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `etoro-public:partner:registration:read` / `:write`                                                        | Registration eligibility checks and registering the user (`/api/v1/registration/users`)                                        |
| `etoro-public:kyc:*` — `read`, `write`, `questions:read`, `answers:read` / `:write`, `regulations:read`    | The KYC journey: questionnaire, required fields, documents, screening, electronic verification, gaps                           |
| `etoro-public:verification.*` — `personaldetails`, `address`, `email`, `phone`, and the `trusted` variants | Personal details and address, and email/phone verification and confirmation                                                    |
| `etoro-public:money.accounts:read` / `:write`                                                              | Cash account creation, eligibility, and status                                                                                 |
| `etoro-public:money.ftd:read` / `:write`                                                                   | First-time deposit records, and the deposit notifications described in [Reporting events to eToro](#reporting-events-to-etoro) |
| `etoro-public:money.deposit.crypto:*`, `money.withdraw.crypto:*`                                           | Crypto deposit intents and crypto withdrawals                                                                                  |
| `etoro-public:sub-accounts:read` / `:write` / `:delete`                                                    | Sub-accounts, and the non-interactive tokens described above                                                                   |
| `etoro-public:real:read`, `etoro-public:trade.real:*`, `etoro-public:demo:read`                            | Portfolio and trading operations in the [Core API](/) with the same token                                                      |

Two further constraints apply in addition to the per-operation scope check, and should be accounted for at design time:

1. **Scopes are granted per partner.** `GET /api/v1/sso/scopes` returns the catalog your client may request — a scope outside it is rejected at registration, not at call time.
2. **A scope is permission to call, not permission to receive PII.** Where an operation would return personal data, the data-sharing approval below governs what comes back. Holding `etoro-public:kyc:read` is not by itself entitlement to a user's personal details.

Partners should request the narrowest scope set that completes the journey being implemented, and extend it only deliberately. The scopes held by a stored non-interactive token define the impact of any compromise of that credential.

## Reporting events to eToro

The integration is bidirectional. Alongside the operations a partner invokes on a user's behalf, eToro requires the partner to **report events that occur on the partner platform** but that eToro must know about — currently, deposits made by an associated user.

Event reporting uses the same authenticated API surface as the rest of the integration. It is a partner-to-eToro call, not a webhook: the partner is the caller, the event is the request body, and the user the event concerns is identified by the credential presented.

### Deposit notification

`POST /api/v1/money/deposit/notify` reports an approved deposit. eToro processes the notification asynchronously and determines from it whether the deposit is the user's first — the first-time deposit (FTD) — at both account and global level.

<ResponseField name="Scope" type="etoro-public:money.ftd:write">
  Required on the token presented. The user the deposit belongs to is the user the credential identifies — the `x-user-key` non-interactive token issued for that user.
</ResponseField>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://public-api.etoro.com/api/v1/money/deposit/notify" \
    -H "X-Request-ID: <UUID>" \
    -H "x-api-key: <YOUR_API_KEY>" \
    -H "x-user-key: <NIT>" \
    -H "Content-Type: application/json" \
    -d '{
          "accountId": "ACC-14540678",
          "transactionId": "TXN-4783212",
          "externalTransactionId": "STRIPE-4e7dc7ff246b44af9907",
          "transactionDate": "2026-09-14T10:05:08Z",
          "inputAmount":     { "amount": 850.0,  "currency": "EUR" },
          "feeAmount":       { "amount": 5.0,    "currency": "USD", "amountInUsd": 5.0 },
          "convertedAmount": { "amount": 1000.0, "currency": "USD" },
          "paymentMethodType": "ach",
          "depositType": "regular"
        }'
  ```

  ```json Response 202 theme={null}
  {
    "success": true,
    "data": {
      "transactionId": "TXN-4783212",
      "message": "Deposit notification accepted for processing"
    }
  }
  ```
</CodeGroup>

| Field                   | Required | Notes                                                                                                                                      |
| ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `accountId`             | Yes      | The account identifier within the partner system                                                                                           |
| `transactionId`         | Yes      | The partner's own unique identifier for the deposit. Echoed back in the acknowledgement, and the key for any later reconciliation          |
| `externalTransactionId` | No       | The payment provider's reference, where one exists                                                                                         |
| `transactionDate`       | Yes      | When the deposit was **approved**, in UTC                                                                                                  |
| `inputAmount`           | Yes      | `amount` and ISO 4217 `currency` as the user paid them                                                                                     |
| `convertedAmount`       | Yes      | `amount` and `currency` after conversion, typically USD                                                                                    |
| `feeAmount`             | No       | Transaction fee, with an optional `amountInUsd`; calculated by eToro when omitted                                                          |
| `paymentMethodType`     | No       | The method used — `creditCard`, `wireTransfer`, `ach`, `openBanking`, `eToroMoney`, and others; see the reference for the full enumeration |
| `depositType`           | No       | `regular` (default) or `recurring`                                                                                                         |
| `extraData`             | No       | Free-form key-value data specific to the partner or account type                                                                           |

### Operational rules

<Steps>
  <Step title="Report on approval, not on initiation">
    `transactionDate` is the approval timestamp. A deposit that is pending, declined, or subsequently reversed is not an approved deposit and should not be reported as one.
  </Step>

  <Step title="Report every approved deposit">
    Do not attempt to determine locally which deposit is the first. Report each approved deposit; eToro evaluates FTD status — account-level and global — from the notifications it receives.
  </Step>

  <Step title="Treat 202 as accepted, not as recorded">
    The response acknowledges that the notification was accepted for background processing. It is not a statement that FTD status has been assigned. Read the outcome back through the FTD endpoints below.
  </Step>

  <Step title="Send x-request-id on every call">
    The header is optional on this operation and required on the FTD read operations. Send a fresh UUID on all of them: it is what makes a notification traceable across both systems when a partner and eToro disagree about whether an event arrived.
  </Step>

  <Step title="Keep personal data out of extraData">
    `extraData` accepts arbitrary keys, which makes it the easiest place to leak PII into an integration that is otherwise token-only. Restrict it to the operational values eToro has agreed with you.
  </Step>
</Steps>

Error responses carry a structured body — `success: false` with an `error` object containing `code`, `message`, `details`, and the offending `field`:

| Status | Meaning                                                                      |
| ------ | ---------------------------------------------------------------------------- |
| `400`  | Malformed request                                                            |
| `401`  | Missing or invalid `x-api-key` / `x-user-key`                                |
| `403`  | The partner is not authorized for this account type                          |
| `422`  | The request is well-formed but semantically rejected — inspect `error.field` |
| `429`  | The shared rate limit (60 requests / 60 seconds) was exceeded                |

### Reading first-time deposit status

Two read operations expose what eToro concluded, both requiring `etoro-public:money.ftd:read`:

| Operation                                                     | Returns                                                                                                                                                                                                   |
| ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /api/v1/money/first-time-deposit`                        | The user's global FTD — their first-ever deposit across all eToro account types, with the account type and identifier it occurred on                                                                      |
| `GET /api/v1/money/accounts/{accountType}/first-time-deposit` | The FTD record or records for one account type (`trading`, `options`, `cash`, `moneyFarm`), optionally filtered by `accountId`. A user may hold several accounts of one type, so the response is an array |

The account-level response distinguishes `isAccountFtd` from `isGlobalFtd`, so a deposit can be the first on a given account while not being the user's first at eToro. Reconcile on `transactionId` — the same value the partner sent in the notification.

<Note>
  Deposit notification is the event type currently published for partner-to-eToro reporting. Additional event types are provisioned per partner; where your integration agreement covers reporting beyond deposits, confirm the applicable contract with your eToro contact rather than inferring one.
</Note>

## Data sharing and PII

**eToro does not release personal information about its users to partners.** That includes, and is not limited to, email address, full name, phone number, date of birth, national identifiers, and address. None of it is available to your application by default, and the absence is deliberate rather than an omission in this guide.

Where a specific integration requires a specific field, that field is released only after the data sharing has been **explicitly approved by eToro for the integration**, and only through the scopes granted to the client for that purpose. The KYC, verification, and personal-details operations in the [Partners API reference](/partners-api-reference) are gated exactly this way: registered scope, approved purpose, per-partner grant. Absent that approval, calling them returns nothing you are not entitled to see.

The consequence for integration design is that the integration is **token-only**:

* **Identity is the `sub` claim.** Pairwise, pseudonymous, stable for your client, and meaningless to anyone else. Use it as the primary key of the association.
* **Authorization is the scope set** on the access token or NIT. Never infer entitlement from anything a user typed.
* **Linkage is `externalUserId` and `subAccountId`.** Opaque handles, both sides of the mapping.
* **No PII joins.** Do not build reconciliation, deduplication, or support lookups on email, name, or phone. If your platform needs a display value, use one your own user supplied to you directly.
* **Do not request claims you have not been approved for**, and do not treat any claim that does appear as licence to store it beyond the purpose it was approved for.
* **Do not push PII to eToro either** unless the endpoint's contract calls for it, in which case it is covered by that endpoint's scope and approval. This applies to reported events as much as to requests: `extraData` on a deposit notification is not a channel for personal data.

Where a product requirement cannot be satisfied on a token-only basis, it must be raised with your eToro contact as a data-sharing request before implementation begins. It is not to be addressed by working around the constraint in code.

## Security checklist

In addition to the [SSO security checklist](/core/getting-started/sso-oauth#security-checklist), the following must be in place before a partner integration is promoted to production:

* Non-interactive tokens issued with the narrowest scope set, an `expiresAt`, and an `ipsWhitelist`.
* NITs held in a secret store — never in source, never in a repository, never in a log line, never in an error payload, never returned to the browser.
* A working revocation path for every NIT, wired to your own user-offboarding flow.
* Egress addresses of every environment that calls the API provided to eToro for allow-listing.
* Account association established solely by eToro-issued tokens, never by matching personal attributes.
* Deposit notifications sent on approval only, with a stable `transactionId` retained for reconciliation.
* No PII in the account association, the reported events, the logs, or the analytics.

## Reference

<CardGroup cols={2}>
  <Card title="SSO with OAuth 2.0" icon="right-to-bracket" href="/core/getting-started/sso-oauth">
    The sign-in flow this guide builds on: registration, PKCE, code exchange, ID token validation.
  </Card>

  <Card title="Partners API reference" icon="code" href="/partners-api-reference">
    Every Partners API endpoint, with the scopes each one requires.
  </Card>

  <Card title="API keys and headers" icon="key" href="/core/getting-started/authentication">
    The `x-api-key` / `x-user-key` pair and request header format.
  </Card>

  <Card title="Rate limits" icon="gauge" href="/core/getting-started/rate-limits">
    Quotas that apply to every call in this flow.
  </Card>
</CardGroup>
