> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.id.me/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.id.me/_mcp/server.

# Integration

> Integrate OpenID Connect with ID.me by setting up your developer account, evaluating well-known endpoints, and completing the authorization flow.

## **ID.me developer account**

To get started, create [an ID.me developer account](https://developers.id.me), set up your organization, and register a new application. This will generate your `client_id` and `client_secret`. Once your application is registered, you can configure one or more `redirect_uri` values to define where ID.me should return the `authorization_code`.

## **Retrieve configuration metadata from well-known endpoints**

Use the well-known endpoint to retrieve standardized configuration metadata. This JSON-formatted response includes critical details such as:

* The issuer URL
* Supported authentication methods
* Token endpoint
* Public key for verifying tokens
* Other essential configuration settings

This metadata allows you to configure your application for secure authentication and authorization, ensuring compatibility with the OpenID Connect (OIDC) standard

## Environments

ID.me provides two environments:

#### Sandbox

`https://api.idmelabs.com/`

#### Production

`https://api.id.me/`

#### Important

Ensure you are using the correct environment URL while working through the following steps

## Direct users to authorization endpoint

To start the OAuth process, your client application must redirect the user to the ID.me authorization endpoint. There, the user signs in and
chooses whether to grant your application access.

```html Example
https://api.id.me/oauth/authorize?client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&response_type=code&scope=openid%20SCOPE&state=optional&eid=FOO-bar-123
```

```html PKCE
https://api.id.me/oauth/authorize?client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&response_type=code&scope=openid%20SCOPE&state=optional&eid=FOO-bar-123&code_challenge=CODE_CHALLENGE&code_challenge_method=S256
```

#### Parameters

| Name                    | Description                                                                                                                                                        | Supported values                                                        |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------- |
| client\_id              | The client identifier received during app registration. It is automatically generated and located in your application dashboard                                    |                                                                         |
| scope                   | Specifies the policy you are requesting access to. To initiate an OpenID Connect transaction, the scope must include openid along with one of the supported values | [View all values](/integrations/configurations/configuration-standards) |
| redirect\_uri           | Where the user is redirected after authorizing an app. Set by the developer within the application dashboard.                                                      |                                                                         |
| response\_type          | Determines the authorization type                                                                                                                                  | `code`                                                                  |
| state                   | An optional parameter used to pass server-specific state, such as a CSRF token. This value is returned to your redirect URI unchanged.                             |                                                                         |
| op                      | An optional parameter that triggers                                                                                                                                | `signin`, `signup`                                                      |
| eid                     | An optional parameter to carry through any external identifiers you would like to receive back in the payload response                                             |                                                                         |
| code\_challenge         | PKCE challenge                                                                                                                                                     |                                                                         |
| code\_challenge\_method | Challenge method                                                                                                                                                   | `S256`                                                                  |

## Receive authorization code

When a user completes the authorization process on ID.me, they will be redirected to your `redirect_uri` with the authorization code parameter appended.

```html Redirect URI with code example
https://example.com/callback?code=488e864b
```

## Exchange authorization code for token payload

Using the authorization code from the previous step, send a request to ID.me's Token Endpoint (see below) to retrieve the payload containing your
`access_token` and `refresh_token`. Each token's expiration can be found within the payload.

* **Endpoint**: `https://api.id.me/oauth/token`
* **Request method**: `POST`
* **Response type**: application/JSON

#### Parameters

| Name           | Description                                                                                                                      |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| code           | The authorization code you received in the previous step                                                                         |
| client\_id     | The client ID assigned during app registration. It is automatically generated and available in your application dashboard.       |
| client\_secret | A secret identifier assigned during app registration. It is automatically generated and available in your application dashboard. |
| redirect\_uri  | Where the user is redirected after authorizing an app. Set by the developer within the application dashboard.                    |
| grant\_type    | The only supported value is `authorization_code`                                                                                 |
| code\_verifier | PKCE verifier used to complete the code exchange (required when PKCE is used)                                                    |

```CURL Example
curl -X POST -d "code=488e864b&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&redirect_uri=REDIRECT_URI&grant_type=authorization_code" https://api.id.me/oauth/token
```

```CURL PKCE

curl -X POST https://api.id.me/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=AUTHORIZATION_CODE" \
  -d "client_id=CLIENT_ID" \
  -d "client_secret=CLIENT_SECRET" \
  -d "redirect_uri=REDIRECT_URI" \
  -d "code_verifier=CODE_VERIFIER"


```

#### Important

Replace the **CLIENT\_ID**, **CLIENT\_SECRET**, **REDIRECT\_URI**, and **SCOPE** with your inputs

## Obtain ID token

#### Parameters

| Name                 | Description                                                                                                            |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| access\_token        | A credential that is used with every API call to ensure ID.me recognizes that you have authorization to make a request |
| id\_token            | A JSON Web Token (JWT)                                                                                                 |
| token\_type          | Represents how an `access_token` will be generated and presented for resource access calls                             |
| expires\_in          | Describes the lifetime of the `access_token` in seconds                                                                |
| refresh\_token       | Contains the information required to obtain a new `access_token`                                                       |
| refresh\_expires\_in | Describes the lifetime of the `refresh_token` in seconds                                                               |
| scope                | Defines the policy you are requesting permission to access                                                             |

```json Example payload
{
  "access_token" : "a0b1c2d3f4g5h6i7j8k9l0m1n2o3p4q5",
  "token_type" : "bearer",
  "expires_in" : "300",
  "refresh_token" : "e7c77fe1fd5ece9aaccb129f6dd39431",
  "refresh_expires_in" : "604800",
  "scope" : "military",
  "id_token" : "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IktBTGVzSUpVVTRuQ0FjLVJTdTJkS0xkYkgtQmFrdXRnekE1VjdoQm9RUm8ifQ.eyJpc3MiO\niJodHRwczovL2FwaS5pZG1lbGFicy5jb20vb2lkYyIsInN1YiI6ImYxNjljMzRkMDA3YjQ1MTBhNzNiYTc5OThjMDgxZWEwIiwiYXVkIjoiZTE3OWUzNT\nc3NTAzMjBlMmU2NTJjODBiNjFjNGRjMDIiLCJleHAiOjE2MjU1MTI3ODQsImlhdCI6MTYyNTQ5NDc4NCwiZW1haWwiOiJ0ZXN0aW5nQGlkLm1lIiwiZm5\nhbWUiOiJURVNUIiwibG5hbWUiOiJVU0VSIiwiemlwIjoiMjIxMDIiLCJ1dWlkIjoiZjE2OWMzNGQwMDdiNDUxMGE3M2JhNzk5OGMwODFlYTAifQ.Qwdnb\nAn6kbnzSvASa8qEMTJO-T-jzkJAfJdLViX188N2ny1DLBjn1AxgsydXmnzeyCtfv9Mn-rjLFtsEAPXMbWoA5maU0Lqt03hbSEbvevksr6xzD0j9mzQdWb\n3YXHkSS-A3dKkl4KM5TiO7BY8W5Xmhp0YivtcW2_C24xdxukYBgR1Y6lmjaKS3SGQRkjO31mj8_qxlAP4RuC9U2cmHx-w2HYVJfwpyauzmM8uo6CD3Ql3\nqCP88bcyJtF2O9cEpvW9E47CcaueGLjBF9_Qe0u1IWzcX77rCTnxMounfjmYDn7Md0JAl6-Q6E23yu-Zibg8CFytmDDm4pGbEA7g8BA"
}
```

## Validate ID token

To ensure the ID token was issued by a trusted source, you need to validate its JWT signature.

ID.me signs the token using the RS256 algorithm. To verify the signature, retrieve the public key from the OpenID Connect server.
You can fetch this key from the JWKS endpoint:

```html JWKS endpoint
https://api.idmelabs.com/oidc/.well-known/jwks
```

This endpoint is referenced in the OIDC discovery document and available in the configuration settings of your developer dashboard.

```json Example JSON Web Token (JWT)
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IktBTGVzSUpVVTRuQ0FjLVJTdTJkS0xkYkgtQmFrdXRnekE1VjdoQm9RUm8ifQ.eyJpc3MiO
iJodHRwczovL2FwaS5pZG1lbGFicy5jb20vb2lkYyIsInN1YiI6ImYxNjljMzRkMDA3YjQ1MTBhNzNiYTc5OThjMDgxZWEwIiwiYXVkIjoiZTE3OWUzNT
c3NTAzMjBlMmU2NTJjODBiNjFjNGRjMDIiLCJleHAiOjE2MjU1MTI3ODQsImlhdCI6MTYyNTQ5NDc4NCwiZW1haWwiOiJ0ZXN0aW5nQGlkLm1lIiwiZm5
hbWUiOiJURVNUIiwibG5hbWUiOiJVU0VSIiwiemlwIjoiMjIxMDIiLCJ1dWlkIjoiZjE2OWMzNGQwMDdiNDUxMGE3M2JhNzk5OGMwODFlYTAifQ.Qwdnb
An6kbnzSvASa8qEMTJO-T-jzkJAfJdLViX188N2ny1DLBjn1AxgsydXmnzeyCtfv9Mn-rjLFtsEAPXMbWoA5maU0Lqt03hbSEbvevksr6xzD0j9mzQdWb
3YXHkSS-A3dKkl4KM5TiO7BY8W5Xmhp0YivtcW2_C24xdxukYBgR1Y6lmjaKS3SGQRkjO31mj8_qxlAP4RuC9U2cmHx-w2HYVJfwpyauzmM8uo6CD3Ql3
qCP88bcyJtF2O9cEpvW9E47CcaueGLjBF9_Qe0u1IWzcX77rCTnxMounfjmYDn7Md0JAl6-Q6E23yu-Zibg8CFytmDDm4pGbEA7g8BA
```

View on JWT.IO

## Decode ID token for user payload

The ID token contains details about the user and their authentication status. The following steps explain how to decode the token,
which is formatted as a JSON Web Token (JWT), to extract the necessary user information.

### Decoded header payload

#### Key descriptions

| Name | Description                                                                                                                                                                       |
| ---- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| typ  | The *Type Header Parameter* is used by JWT applications to declare the media type                                                                                                 |
| alg  | The *Algorithm Header Parameter* is used by JWT applications to declare the algorithm type                                                                                        |
| kid  | The *Key Identifier* is an optional header that holds a key identifier that is useful when you have multiple signing keys and need to locate the correct one to verify signatures |
| iss  | The *Issuer* identifies the principal that issued the JWT                                                                                                                         |
| sub  | The *Subject* is the identifier for the end user (the subject of the token)                                                                                                       |

```json Decoded header payload example
{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "KALesIJUU4nCAc-RSu2dKLdbH-BakutgzA5V7hBoQRo",
  "iss": "https://api.idmelabs.com/oidc",
  "sub": "f169c34d007b4510a73ba7998c081ea0"
}
```

### Decoded ID token payload

#### Key descriptions

| Name | Description                                                                                                                        |
| ---- | ---------------------------------------------------------------------------------------------------------------------------------- |
| aud  | *Audience* identifies the recipients that the JWT is intended for and the value should match the client who is receiving the token |
| exp  | *Expiration time* indicates when the token will expire (measured in seconds)                                                       |
| iat  | *Issued at* indicates when the token will expire (measured in seconds)                                                             |
| uuid | A *unique identifier* associated with the member's ID.me account                                                                   |

```json Decoded ID token payload example
{
  "aud": "8749d197447c364b219afbd4b613ebd0",
  "exp": 1616364069,
  "iat": 1616346069,
  "email": "rod.hamill@id.me",
  "fname": "Rod",
  "lname": "Hamill",
  "street": "72146 Okuneva Heights",
  "city": "Jeffereyburgh",
  "state": "Hawaii",
  "zip": "94468-5006",
  "birthdate": "1959-12-08",
  "eid": "FOO-bar-123",
  "uuid": "d733a89e2e634f04ac2fe66c97f71612"
}
```

## Exchange access token for ID token

This step is ***optional*** and only necessary if you would like to use an ID token in place of the access token.

* **Endpoint**: `https://api.id.me/api/public/v3/userinfo`
* **Request method**`GET`
* **Response type** application/JSON

#### Parameters

| Name          | Description                                                                                                           |
| ------------- | --------------------------------------------------------------------------------------------------------------------- |
| access\_token | A token included with every API request that allows ID.me to verify your authorization to access protected resources  |
| callback      | If your AJAX application requires a JSONP response, include a callback parameter in the API call to wrap the response |

```curl Example
curl -X GET -d "access_token=488e864b" https://api.id.me/api/public/v3/userinfo
```

```json Example payload 
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IktBTGVzSUpVVTRuQ0FjLVJTdTJkS0xkYkgtQmFrdXRnekE1VjdoQm9RUm8ifQ.eyJpc3MiO
iJodHRwczovL2FwaS5pZG1lbGFicy5jb20vb2lkYyIsInN1YiI6ImYxNjljMzRkMDA3YjQ1MTBhNzNiYTc5OThjMDgxZWEwIiwiYXVkIjoiZTE3OWUzNT
c3NTAzMjBlMmU2NTJjODBiNjFjNGRjMDIiLCJleHAiOjE2MjU1MTI3ODQsImlhdCI6MTYyNTQ5NDc4NCwiZW1haWwiOiJ0ZXN0aW5nQGlkLm1lIiwiZm5
hbWUiOiJURVNUIiwibG5hbWUiOiJVU0VSIiwiemlwIjoiMjIxMDIiLCJ1dWlkIjoiZjE2OWMzNGQwMDdiNDUxMGE3M2JhNzk5OGMwODFlYTAifQ.Qwdnb
An6kbnzSvASa8qEMTJO-T-jzkJAfJdLViX188N2ny1DLBjn1AxgsydXmnzeyCtfv9Mn-rjLFtsEAPXMbWoA5maU0Lqt03hbSEbvevksr6xzD0j9mzQdWb
3YXHkSS-A3dKkl4KM5TiO7BY8W5Xmhp0YivtcW2_C24xdxukYBgR1Y6lmjaKS3SGQRkjO31mj8_qxlAP4RuC9U2cmHx-w2HYVJfwpyauzmM8uo6CD3Ql3
qCP88bcyJtF2O9cEpvW9E47CcaueGLjBF9_Qe0u1IWzcX77rCTnxMounfjmYDn7Md0JAl6-Q6E23yu-Zibg8CFytmDDm4pGbEA7g8BA"
```

## Parse JSON response

Parsing the JSON response accurately is essential for building a scalable and reliable integration. How your application
retrieves and handles this data will determine whether it can adapt to changes, such as the addition of new attributes, without breaking.

#### Best practice

* Use the *handle* field to index attribute values

* Validate only attributes that are included in the response

* Store raw JSON responses for auditing and troubleshooting

* Create a database table to store verification status, `uuid`, and authoritative data from the ID.me API

* Use the `uuid` as a foreign key to link user activity across your application

* Pre-fill form fields to improve the user experience after verification

* Do not assume attributes or status values will be returned in a specific order

#### Example data types

The type of each attribute determines how data will be returned from the ID.me API. You may receive any of the following types.

```json String
{
  "email": "testing@id.me"
}
```

```json Integer
{
  "age": 21
}
```

```json Object
{
  "previous_addresses": [
    {
      "normalized_street": "8281 Greensboro Drive",
      "street1": "8281 Greensboro Drive",
      "street2": "",
      "city": "West McLean",
      "state": "VA",
      "province": "",
      "zip": "22102",
      "country": "US",
      "normalized": "false",
      "primary": "false"
    },
    {
      "normalized_street": "6647 WILDFLOWER DR S",
      "street1": "6647 WILDFLOWER DR S",
      "street2": "",
      "city": "COTTAGE GROVE",
      "state": "MN",
      "province": "",
      "zip": "55016",
      "country": "US",
      "normalized": "false",
      "primary": "false"
    }
  ]
}
```

#### Important

Do **NOT** assume that all attribute values will be strings.

Per the OpenID Connect Core 1.0 specification, if an attribute is not available, it is **omitted** from the UserInfo JSON response rather than returned with a `null` or empty string value. Your code should check for the presence of a key before accessing it, and treat an absent key as "not provided".

The only attribute guaranteed to always be present is `sub` (the subject identifier).

#### Best practice

It is best practice to develop code in a manner that gracefully handles variations in data. For example, a name that contains a hyphen.

**Payload specification**

| Handle            | Name              | Max Length | Optional | Comment                                                                                   |
| ----------------- | ----------------- | ---------- | -------- | ----------------------------------------------------------------------------------------- |
| email             | Email             | 255        | No       |                                                                                           |
| uuid              | Unique identifier | 32         | No       |                                                                                           |
| fname             | First Name        | 255        | Yes      | Individuals with only 1 legal name would have that name placed in the Last Name attribute |
| mname             | Middle Name       | 255        | Yes      |                                                                                           |
| lname             | Last Name         | 255        | No       |                                                                                           |
| birth\_date       | Birth Date        | 10         | No       | Date format: YYYY-MM-DD                                                                   |
| social            | Full SSN          | 9          | Yes      |                                                                                           |
| itin              | Full ITIN         | 9          | Yes      |                                                                                           |
| ssn\_itin         | Full SSN or ITIN  | 9          | Yes      | If a member has both SSN and ITIN, SSN takes precedence                                   |
| street            | Street            | 255        | Yes      | 123 Main St Apt 1a                                                                        |
| street1           | Street1           | 255        | Yes      | 123 Main St                                                                               |
| street2           | Street2           | 255        | Yes      | Apt 1a                                                                                    |
| city              | City              | 255        | Yes      |                                                                                           |
| state             | State             | 255        | Yes      | Abbreviation or full state                                                                |
| zip               | Postal Code       | 10         | Yes      | US Postal format: 00000, 00000-0000, or 000000000                                         |
| phone             | Phone             | 11         | Yes      | Phone format: 10000000000                                                                 |
| emails\_confirmed | Confirmed Emails  | n/a        | No       | Array of string values                                                                    |

International users will have a higher rate of omitted address attributes.

Additional attributes may be available. Your ID.me team will be happy to discuss your specific use case.