> For the complete documentation index, see [llms.txt](https://docs.truecaller.com/truecaller-sdk/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.truecaller.com/truecaller-sdk/android/oauth-sdk-3.1.0/integration-steps/integrating-with-your-backend/fetching-user-token.md).

# Fetching User Token

Using the “state” from step 10, “code verifier” from step 12, and the “authorization code” from step 9, you need to make a network call to Truecaller’s backend so as to fetch the access token :

`POST` `https://oauth-account-noneu.truecaller.com/v1/token`

**Headers**

| Name           | Type                              | Description |
| -------------- | --------------------------------- | ----------- |
| Content-Type\* | application/x-www-form-urlencoded |             |
|                | String                            |             |

**Request Body**

| Name           | Type                         | Description                                              |
| -------------- | ---------------------------- | -------------------------------------------------------- |
| grant\_type    | "authorization\_code"        | // hardcoded value                                       |
|                | String                       |                                                          |
| client\_id     | \<YOUR\_CLIENT\_ID>          |                                                          |
| code           | \<USER\_AUTHORISATION\_CODE> | Authorisation code from TcOAuthData callback from step 9 |
| code\_verifier | \<YOUR\_CODE\_VERIFIER>      | From step 12                                             |

```json
curl --location --request POST 'https://oauth-account-noneu.truecaller.com/v1/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'client_id=<<your-client-id>>' \
--data-urlencode 'code=<<authorization_code>>' \
--data-urlencode 'code_verifier=<<your-code-verifier>>'
```

{% tabs %}
{% tab title="200: OK Success " %}

```json
{
  "access_token": "o1a04--7JZl5G--VfhYWSSRqC0K-Xfq9lmeaKw3xlrdBluXBAIiVI",
    "expires_in": 3600,
    "scope": "openid profile",
    "token_type": "Bearer",
    "id_token": "eyJhbGciOiJSUzI1Ni..."
  }
```

{% endtab %}
{% endtabs %}

&#x20;**Case 1: Generating the token WITH offline\_access for first time.**\
App requested openid profile offline\_access (both the Portal toggle and setOAuthScopes() must include it).&#x20;

```json
curl --location --request POST 'https://oauth-account-noneu.truecaller.com/v1/token' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'client_id=<<your-client-id>>' \
--data-urlencode 'code=<<authorization-code>>' \
--data-urlencode 'code_verifier=<<pkce-code-verifier>>'
```

```json
{
"access_token": "o1a04--7JZl5G--VfhYWSSRqC0K-Xfq9lmeaKw3xlrdBluXBAIiVI",
  "expires_in": 3600,
  "scope": "openid profile offline_access",
  "token_type": "Bearer",
  "id_token": "eyJhbGciOiJSUzI1Ni...",
  "refresh_token": "eyJhbGciOiJSUzI1Ni...def456"
}
```

**Case 2: Using the refresh token to get a NEW access token (offline\_access flow, on return)**\
When the user comes back and you need a fresh access token, call the same endpoint with grant\_type=refresh\_token. Do not send code or code\_verifier.

```json
  curl --location --request POST 'https://oauth-account-noneu.truecaller.com/v1/token' \
  --data-urlencode 'grant_type=refresh_token' \
  --data-urlencode 'client_id=<<your-client-id>>' \
  --data-urlencode 'refresh_token=<<stored-refresh-token>>'
```

**Response —**  The API returns a new access token and a new refresh token (rotated). Please note that the `scope` field will be returned as empty in the response.<br>

```
{
    "access_token": "p9c72--2KQm8H--WgiZXTTStdD1L-Ygr0mnfbLx4ymsecCmvYCJjWJ",
    "expires_in": 3600,
    "scope": "",
    "token_type": "Bearer",
    "id_token": "eyJhbGciOiJSUzI1Ni...",
    "refresh_token": "eyJhbGciOiJSUzI1Ni...ghi789"
  }
```

{% hint style="warning" %}
**Replace your stored refresh token with this new one. Reusing an old refresh token invalidates the whole session.**
{% endhint %}

{% hint style="info" %}
**1:- Refresh token TTL is 14 days, but the token family has an absolute limit of 180 days from the initial token’s creation.** <br>

```
For example:
Day 0: User authenticates → refresh token R1 is issued.
Day 10: App uses R1 → receives a new access token and refresh token R2, valid for another 14 days.
Day 20: App uses R2 → receives R3, valid for another 14 days.
This rotation can continue, but the original Day 0 timestamp stays attached to the family.
Day 179: App refreshes successfully and receives another refresh token.
Day 181: That token is still within its individual 14-day validity, but refreshing fails because the family is older than 180 days
```

2 :-**Whenever new refresh token is used to obtain access token, that will remain active only for 30 seconds after that and can be used 8 times in that 30 seconds window.**\
**suppose we have used R1 to make R2, R1 will remain active for 30 seconds and R2 will have TTL of 14 days. If in 30 seconds we have again used R1, then it will generate R3. R2 will be deleted and no longer useful. R3 will be latest. But R1 can only be used 8 times.**
{% endhint %}

\
400: Bad Request -  If grant type is not supported\
403: Forbidden - If client id is invalid\
500: Internal Server Error - Unexpected error on the server side\
400: Bad Request  - Some of the parameters are empty in the request\
403: Forbidden Valid grant type but not allowed for the client\
403: Forbidden Invalid auth code provided\
403: Forbidden Invalid/expired auth code in provided\
403: Forbidden Invalid/expired code verifier is provided\
429: Too Many Requests If the number of requests exceeds the allowed limit\
503: Service Unavailable Resource unavailable due to server-side issue
