# Authenticating users with the portal API

### Overview

API key authentication allows you to automatically sign in users by including an authentication token in the URL. This is useful for:

* Creating magic login links
* Embedding quests in external applications
* Deep-linking directly to protected content

### How It Works

When a valid API key is provided as a query parameter in the URL, the system will:

1. Validate the key against our backend
2. If valid, automatically sign in the associated user
3. Redirect to the same URL but with the token removed (for security)
4. **Store the user session in a cookie**, allowing continued access without the API key

Once the user has been authenticated, **they remain signed in via the session cookie**, not the API key. This means:

* The API key is only needed for the initial authentication
* You can use very short expiration times for your API keys (even minutes or seconds)
* After authentication, the user has a normal session just like any other login method

### Generating API Keys

To generate an API key, use the External User Lookup API endpoint on your community's domain:

```
POST https://your-community-slug.domino.page/api/auth/external-lookup
```

> **Important:** Replace `your-community-slug` with your actual community slug (e.g., if your community is at `acme.domino.page`, use that domain for the API request).

#### Request Parameters

| Parameter             | Type   | Description                                                                                                          |
| --------------------- | ------ | -------------------------------------------------------------------------------------------------------------------- |
| `externalId`          | String | **Required**. The unique identifier for this user in your system                                                     |
| `keyName`             | String | Optional. A name for this API key (defaults to "External API Key for \[community]")                                  |
| `keyExpiresInDays`    | Number | Optional. How long the key should be valid in days (defaults to 30 days)                                             |
| `keyExpiresInSeconds` | Number | Optional. How long the key should be valid in seconds. Takes precedence over `keyExpiresInDays` if both are provided |

#### User Profile Data

You can pass user profile information to create or update the user:

| Parameter   | Type    | Description                                                                                                   |
| ----------- | ------- | ------------------------------------------------------------------------------------------------------------- |
| `name`      | String  | Display name for the user                                                                                     |
| `email`     | String  | User's email address                                                                                          |
| `imageUrl`  | String  | Profile picture URL (will be cached to our CDN)                                                               |
| `overwrite` | Boolean | When `true`, provided fields overwrite existing data. When `false` (default), only empty fields are populated |

#### Social Account Identifiers

You can link social accounts by providing their platform IDs:

| Parameter     | Type   | Description       |
| ------------- | ------ | ----------------- |
| `discordId`   | String | Discord user ID   |
| `twitterId`   | String | Twitter/X user ID |
| `telegramId`  | String | Telegram user ID  |
| `redditId`    | String | Reddit user ID    |
| `zealyUserId` | String | Zealy user ID     |

You can also provide usernames as supplementary data:

* `discordUsername`
* `twitterUsername`
* `telegramUsername`
* `redditUsername`

#### Wallet Data

You can link wallets to the user:

| Parameter | Type   | Description                                                     |
| --------- | ------ | --------------------------------------------------------------- |
| `wallet`  | Object | A single wallet: `{ walletAddress, type, network?, provider? }` |
| `wallets` | Array  | Array of wallet objects with the same structure                 |

Wallet object structure:

* `walletAddress` (String): The wallet address
* `type` (String): Wallet type - `SOLANA`, `EVM`, or `TON`
* `network` (String, optional): Network identifier
* `provider` (String, optional): Wallet provider

#### Response

```json
{
  "user": {
    "id": "user-id",
    "username": "username",
    // other user properties
  },
  "apiKey": {
    "id": "key-id",
    "key": "the-actual-api-key",
    "expiresAt": "2023-12-31T23:59:59Z"
  }
}
```

#### Example Request

```javascript
// Replace 'acme.domino.page' with your actual community domain
const response = await fetch('https://acme.domino.page/api/auth/external-lookup', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'your-domino-api-key'
  },
  body: JSON.stringify({
    externalId: 'user123',
    keyName: 'Login link for newsletter',
    keyExpiresInSeconds: 300 // Expires in 5 minutes
  })
});

const data = await response.json();
const apiKey = data.apiKey.key;
```

#### Example with User Profile Data

```javascript
const response = await fetch('https://acme.domino.page/api/auth/external-lookup', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'your-domino-api-key'
  },
  body: JSON.stringify({
    externalId: 'user123',
    // Profile data
    name: 'John Doe',
    email: 'john@example.com',
    imageUrl: 'https://example.com/avatars/john.png',
    // Social accounts
    discordId: '123456789012345678',
    discordUsername: 'johndoe#1234',
    twitterId: '987654321',
    // Wallets
    wallets: [
      { walletAddress: '0x1234...', type: 'EVM' },
      { walletAddress: 'abc123...', type: 'SOLANA' }
    ],
    // Set to true to update existing user data
    overwrite: true,
    // API key options
    keyExpiresInSeconds: 300
  })
});
```

### Using the API Key in URLs

To authenticate a user with an API key, include the key as an `authToken` query parameter in your community's domain:

```
https://acme.domino.page/quests?authToken=the-actual-api-key
```

When the user visits this URL, they will be:

1. Automatically authenticated
2. Redirected to the same URL without the token parameter
3. Granted a session cookie for continued access

### Security Considerations

* API keys are sensitive credentials and should be treated securely
* Each key is linked to a specific user and should not be shared
* **API keys can have very short expiration times** since they're only needed for initial authentication
* Consider using `keyExpiresInSeconds` for one-time use links with very short lifetimes (minutes or even seconds)
* The redirect ensures the token isn't stored in browser history
* After authentication, the normal session cookie security applies


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.domino.run/docs/developer-resources/authenticating-users-with-the-portal-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
