> For the complete documentation index, see [llms.txt](https://docs.maiagent.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.maiagent.ai/tech/en/serve/contacts/contact-credentials-sync.md).

# Contact Identity Synchronization and Token Updates

Use setup-contact-credentials to synchronize contact identities and update token credentials when enterprise systems add users and on each login

After embedding Web Chat (including [MaiGPT mode](/tech/en/serve/web-chat-sdk/web-chat-maigpt-mode.md)) in your product, your enterprise system needs to tell the AI who is asking. This lets the AI retrieve the user's conversation history and access your enterprise system's APIs with **that user's permissions** when calling MCP tools.

The `setup-contact-credentials` API performs two tasks in a single call:

1. **Create or update** the mapping between an enterprise system account and a MaiAgent Contact
2. Write the user's Access Token to the **contact's MCP credentials**, allowing the Agent to call tools on their behalf

{% hint style="info" %}
For the Contact concept and overall integration process, first see [Introduction to Contacts and Integration](/tech/en/serve/contacts.md). If you do not use MCP tools and only need to create contact mappings, you can also use the `POST /api/v1/contacts/` flow described on that page. The API on this page is suitable when you need to update tokens during login.
{% endhint %}

### Choosing Between Frontend auth and Backend Integration <a href="#frontend-vs-backend" id="frontend-vs-backend"></a>

This API also has a frontend option: the embed SDK's `auth` configuration (see [Web Chat Embedding and SDK](/tech/en/serve/web-chat-sdk.md#identify-users)) causes the chat window to call this same API automatically. Both approaches create the same contacts (the same `sourceId` identifies the same person); the only difference is who makes the call:

|                       | Frontend auth (automatic via SDK)                                                          | Backend integration (this page)                                             |
| --------------------- | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------- |
| Caller                | The user's browser                                                                         | Your backend server                                                         |
| Integration effort    | Low: add an `auth` object to config                                                        | Medium: add an API call to the login flow and store `contactId`             |
| Token confidentiality | `mcpCredentials` appears in the frontend page; users can see their own token in the source | Tokens do not pass through the frontend                                     |
| Suitable for          | Identifying users and retaining conversations across devices                               | Binding MCP credentials so the AI can call APIs with the user's permissions |

## 1. When to Call <a href="#when-to-call" id="when-to-call"></a>

| Scenario                           | Approach                                                                                                                                                                                                          |
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Adding a user**                  | Call this API during account creation and store the returned `contactId` in the member table                                                                                                                      |
| **Every login**                    | Call this API after successful login and Access Token generation, before returning the response to the frontend. Send the **new token** (the same `sourceId` triggers an update rather than creating a duplicate) |
| **Backfilling an existing system** | Call this API for each existing user to create contacts; see [Backfilling Existing Users](#backfill)                                                                                                              |
| **Token expiration / refresh**     | Call this API again with the new token                                                                                                                                                                            |
| **Logout**                         | Call SDK `signOut()` on the frontend to return to anonymous mode; delete the contact's tool credentials on the backend. See [Logout and Credential Revocation](#logout-revoke)                                    |

## 2. API Specification <a href="#api-spec" id="api-spec"></a>

| Item           | Value                                                                             |
| -------------- | --------------------------------------------------------------------------------- |
| Method         | `POST`                                                                            |
| URL            | `https://api.maiagent.ai/api/v1/web-chats/{webChatId}/setup-contact-credentials/` |
| Authentication | No API Key required (public endpoint; only the Web Chat ID is needed)             |
| Content-Type   | `application/json`                                                                |
| Rate limit     | 30 requests per IP per minute                                                     |

{% hint style="info" %}
The URL uses the SaaS environment (`api.maiagent.ai`) as an example. For private cloud / on-premises deployments, replace it with your environment's API domain.
{% endhint %}

### Request Body <a href="#request-body" id="request-body"></a>

```json
{
  "sourceId": "user-12345",
  "name": "Wang Xiaoming",
  "mcpCredentials": {
    "toolId": "your-mcp-tool-id",
    "headers": {
      "Authorization": "Bearer <<user Access Token>>"
    }
  }
}
```

| Field                    | Required | Description                                                                                                                                                                                                                                                                |
| ------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sourceId`               | ✅        | The user's unique identifier in your enterprise system. **Repeated calls with the same `sourceId` update the contact instead of creating a duplicate.** Use an unguessable value (such as a UUID); see the security notice below                                           |
| `name`                   |          | The user's display name, shown in the contact list in the MaiAgent admin console; defaults to `Anonymous` if omitted. **Only takes effect when the contact is first created.** For later name updates, use [Synchronizing Contact Profiles](#sync-contact-profile)         |
| `mcpCredentials.toolId`  |          | The ID of the **MCP tool** to bind credentials to (omit the entire `mcpCredentials` object if you do not use MCP tools). Only MCP tools are accepted; passing an API tool ID returns 400. For API tool credentials, see [Two Types of Tool Credentials](#credential-types) |
| `mcpCredentials.headers` |          | HTTP headers to send to the MCP Server so the AI can call tools on the user's behalf                                                                                                                                                                                       |
| `embedOrigin`            |          | The embedding page's Origin. If Web Chat has allowed embedding domains configured, **server-to-server** calls (without a browser Origin header) must include this field to pass validation                                                                                 |

### Response (200 OK) <a href="#response" id="response"></a>

```json
{
  "contactId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
```

**Store the returned `contactId` in the enterprise system's member data** and pass it when initializing Web Chat on the frontend.

{% hint style="danger" %}
**Treat both `sourceId` and `contactId` as identity credentials**: This API is a public endpoint. Anyone who knows `webChatId` and a person's `sourceId` can obtain their `contactId`; anyone with that `contactId` can chat as that user and view their conversation history. Therefore:

* Use **unguessable, non-enumerable** values (such as UUIDs) for `sourceId`, rather than sequential IDs, email addresses, or phone numbers
* Expose `contactId` only to the user on **authenticated** pages; do not put it in public page source
* Configure an **allowed embedding domains** list for Web Chat (administrators enter it in the conversation platform's basic settings in the admin console) to restrict the origins that can call this API (see [Web Chat Embedding and SDK](/tech/en/serve/web-chat-sdk.md#embed-origin-allowlist))
  {% endhint %}

### curl Example <a href="#curl-example" id="curl-example"></a>

```bash
curl -X POST \
  'https://api.maiagent.ai/api/v1/web-chats/{webChatId}/setup-contact-credentials/' \
  -H 'Content-Type: application/json' \
  -d '{
    "sourceId": "user-12345",
    "name": "Wang Xiaoming",
    "mcpCredentials": {
      "toolId": "your-mcp-tool-id",
      "headers": {
        "Authorization": "Bearer eyJhbGciOiJIUzI1NiIs..."
      }
    }
  }'
```

## 3. Where to Integrate in the Login Flow <a href="#login-flow" id="login-flow"></a>

### First Login <a href="#first-login" id="first-login"></a>

```mermaid
sequenceDiagram
    actor U as User
    participant FE as Enterprise system frontend
    participant BE as Enterprise system backend
    participant MA as MaiAgent

    U->>FE: Log in
    FE->>BE: Account and password
    BE->>BE: Verify credentials and generate Access Token
    rect rgb(230, 242, 255)
        Note over BE,MA: New integration step
        BE->>MA: POST setup-contact-credentials<br/>（sourceId＋name＋mcpCredentials）
        MA-->>BE: contactId
        BE->>BE: Store contactId in the member table
    end
    BE-->>FE: Login result (including contactId)
    FE->>MA: Load Web Chat (pass contactId in config)
    MA-->>U: Agent serves the user under their identity
```

### Subsequent Logins / Token Refresh <a href="#subsequent-login" id="subsequent-login"></a>

```mermaid
sequenceDiagram
    actor U as User
    participant BE as Enterprise system backend
    participant MA as MaiAgent

    U->>BE: Log in
    BE->>BE: Generate a new Access Token
    BE->>MA: POST setup-contact-credentials<br/>（same sourceId＋new token）
    Note over MA: Recognize the same contact<br/>Update credentials without creating a duplicate
    MA-->>BE: contactId (same value)
    BE-->>U: Login result (reuse existing contactId)
```

We recommend adding a field to the member table:

| Field name            | Type                         | Description                                                    |
| --------------------- | ---------------------------- | -------------------------------------------------------------- |
| `maiagent_contact_id` | UUID / VARCHAR(36), nullable | Corresponding MaiAgent Contact ID, stored after the first call |

{% hint style="success" %}
**Handling token expiration:** When a token expires or is refreshed, call the same API again with the new token. The same `sourceId` automatically updates the existing contact's credentials without creating a duplicate.
{% endhint %}

{% hint style="warning" %}
**Failures should not block login:** If this API call fails, it should not prevent the user from logging in to your enterprise system. We recommend making the call asynchronously, logging failures, and retrying on the next login.
{% endhint %}

## 4. Backfilling Existing Users <a href="#backfill" id="backfill"></a>

{% hint style="success" %}
**For large backfills, use bulk import instead**: Both the admin console and API support importing users from Excel in one operation (up to 10,000 rows), including query\_metadata and API tool credentials. See [Bulk Contact Import](/tech/en/serve/contacts.md#bulk-import). The per-user calls below are suitable for gradually backfilling through the login flow.
{% endhint %}

When integrating an existing system for the first time, call this API for each existing user to create contact mappings:

```mermaid
flowchart TD
    A[Retrieve the existing user list] --> B["POST setup-contact-credentials<br/>（sourceId＋name, omit mcpCredentials for now）"]
    B --> C[Write contactId back to the member table]
    C --> D{Another user?}
    D -->|Yes| E["Pace requests<br/>（30 requests per IP per minute）"]
    E --> B
    D -->|No| F[Backfill complete]
    F -.-> G["On each user's next login<br/>Add token credentials through the login flow"]
```

* Send each user's `sourceId` and `name`, and write the returned `contactId` back to the member table
* `sourceId` is idempotent: you can safely rerun the backfill script without creating duplicate contacts
* If the user's Access Token is unavailable during backfill, omit `mcpCredentials` and add it through the login flow on the user's next login
* Observe the rate limit (30 requests per IP per minute) and pace requests for large backfills

## 5. Synchronizing Contact Profiles (Optional) <a href="#sync-contact-profile" id="sync-contact-profile"></a>

To synchronize information such as the user's department, role, or service tier with MaiAgent (the Agent automatically reads this information to provide personalized responses), use the Contact API:

| Item           | Value                                                  |
| -------------- | ------------------------------------------------------ |
| Method         | `PATCH`                                                |
| URL            | `https://api.maiagent.ai/api/v1/contacts/{contactId}/` |
| Authentication | `Authorization: Api-Key <<your API Key>>`              |
| Content-Type   | `application/json`                                     |

```json
{
  "name": "Wang Xiaoming",
  "email": "xiaoming.wang@example.com",
  "metadata": [
    {"key": "Department", "value": "Technical Support"},
    {"key": "Role", "value": "Senior Engineer"},
    {"key": "Service Tier", "value": "Premium"}
  ]
}
```

* All fields are optional. PATCH updates only the fields provided; omitted fields are not cleared
* The `metadata` field is **replaced in full**: the supplied array completely overwrites the old one, so send the complete metadata array
* Call this on every login to keep the data current, or only when users update their profiles

<details>

<summary>Why does identity synchronization require no authentication, while profile synchronization requires an API Key?</summary>

`setup-contact-credentials` is a public endpoint that returns only a UUID. Even if called maliciously, it can at most write incorrect credentials—the enterprise system's own authentication mechanism rejects invalid tokens.

However, the AI reads contact data (names, departments, and attributes) and includes it in responses. If this were a public endpoint, an attacker could change a name to arbitrary content and send it directly to the LLM. Contact profile updates therefore require **API Key authentication** to ensure that only authorized backends can modify the data.

</details>

## 6. Logout and Credential Revocation <a href="#logout-revoke" id="logout-revoke"></a>

Logout involves two actions, one on the frontend and one on the backend, with different scopes:

|                  | Frontend `MaiAgent.auth.signOut()`                                                          | Backend tool credential deletion (this section)                                   |
| ---------------- | ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| Effect           | Return the chat window to anonymous mode and clear `contextData`                            | Remove the contact's MCP / API tool credentials from MaiAgent                     |
| Tool credentials | **Does not** alter stored credentials                                                       | Credentials are deleted immediately; the AI can no longer call tools as that user |
| When to call     | When the user logs out on the page (see [SDK API](/tech/en/serve/web-chat-sdk.md#api-auth)) | Call from your backend during logout                                              |

If your system already revokes the Access Token on its own authentication server during logout, the credentials stored in MaiAgent are already invalid. Deleting credentials on the backend adds defense in depth; we recommend doing this as well to avoid leaving invalid tokens in the system.

```mermaid
sequenceDiagram
    actor U as User
    participant FE as Enterprise system frontend
    participant BE as Enterprise system backend
    participant MA as MaiAgent

    U->>FE: Log out
    FE->>MA: MaiAgent.auth.signOut()
    Note over FE,MA: Return the chat window to anonymous mode
    FE->>BE: Logout request
    BE->>BE: Revoke the system's Access Token
    BE->>MA: DELETE the contact's mcp-credentials／api-credentials
    MA-->>BE: 204 No Content
    Note over MA: Credentials removed; contact and conversation history retained<br/>Recreated by the identity synchronization API on next login
```

### Credential Deletion API <a href="#delete-credentials" id="delete-credentials"></a>

| Credential type | Method / URL                                                          |
| --------------- | --------------------------------------------------------------------- |
| MCP tool        | `DELETE /api/v1/contacts/{contactId}/mcp-credentials/{credentialId}/` |
| API tool        | `DELETE /api/v1/contacts/{contactId}/api-credentials/{credentialId}/` |

Authentication uses `Authorization: Api-Key <<your API Key>>` (the same as the Contact API). A successful call returns `204 No Content`:

```bash
curl -X DELETE \
  'https://api.maiagent.ai/api/v1/contacts/{contactId}/mcp-credentials/{credentialId}/' \
  -H 'Authorization: Api-Key <<your API Key>>'
```

The contact, conversation history, and custom attributes are retained after deletion. On the user's next login, calling `setup-contact-credentials` in the login flow recreates the credentials (the same contact and tool pair is updated / recreated without duplication).

### Obtaining credentialId <a href="#get-credential-id" id="get-credential-id"></a>

`setup-contact-credentials` returns only `contactId`, not a credential ID. To obtain `credentialId`:

* Use the `mcpCredentials` and `apiCredentials` arrays in the response from `GET /api/v1/contacts/{contactId}/` (with `Api-Key` authentication). Each entry contains `id` and `tool` information; find the target credential's `id` by matching `tool.id`
* When [creating credentials through the Contact API](#credential-types), the response is the complete contact object, so you can store the credential `id` at creation time

Credential `headers` in responses may be masked depending on the caller's identity. Revocation only needs the `id`, so this does not affect the process.

## 7. Two Types of Tool Credentials <a href="#credential-types" id="credential-types"></a>

Contacts can have dedicated credentials for two types of tools, configured through different entry points:

|                                     | MCP tool credentials                                                                                                        | API tool credentials                                                                          |
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| Purpose                             | Let the AI call an MCP Server as the user                                                                                   | Let the AI call API tools as the user                                                         |
| Synchronization at login            | ✅ `setup-contact-credentials` on this page (no API Key required)                                                            | ✖ Not supported (passing an API tool ID returns 400)                                          |
| Contact API                         | `POST /api/v1/contacts/{contactId}/mcp-credentials/`                                                                        | `POST /api/v1/contacts/{contactId}/api-credentials/`                                          |
| Update / delete a single credential | `PATCH`／`DELETE /api/v1/contacts/{contactId}/mcp-credentials/{credentialId}/`                                               | `PATCH`／`DELETE /api/v1/contacts/{contactId}/api-credentials/{credentialId}/`                 |
| Bulk import                         | ✖                                                                                                                           | ✅ Excel's `API Credentials` column; see [Bulk Import](/tech/en/serve/contacts.md#bulk-import) |
| Admin console UI                    | Edit contact → MCP credentials (see the [User Manual](https://docs.maiagent.ai/build/tools/setup-contacts-mcp-credentials)) | —                                                                                             |

Both credential endpoints in the Contact API require `Api-Key` authentication and use the same body format:

```json
{
  "tool": "<<tool ID>>",
  "headers": { "Authorization": "Bearer <<user token>>" }
}
```

Repeated calls for the same (contact, tool) pair update the credentials rather than creating duplicates. The single-credential update / delete endpoints identify the target using `credentialId`; see [Obtaining credentialId](#get-credential-id).

## 8. Error Handling <a href="#error-handling" id="error-handling"></a>

| HTTP status code | Possible cause                                                                                                                           | Recommended action                                           |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| `400`            | Missing required field (such as `sourceId`); Origin not in the allowed embedding domains list; `toolId` does not exist or is unavailable | Check the request body and Web Chat settings                 |
| `404`            | Web Chat ID does not exist                                                                                                               | Verify that `webChatId` is correct                           |
| `429`            | Rate limit exceeded (30 requests per IP per minute)                                                                                      | Reduce the call frequency and retry                          |
| `500`            | Server error                                                                                                                             | Retry later; contact the MaiAgent team if the error persists |

## 9. Frequently Asked Questions <a href="#faq" id="faq"></a>

<details>

<summary>Will repeated calls for the same user create multiple contacts?</summary>

No. As long as `sourceId` is the same, MaiAgent recognizes the same contact and only updates the credentials.

</details>

<details>

<summary>Do I need to retrieve contactId again on every login?</summary>

Once created, `contactId` does not change and can be stored permanently in the member table. We still recommend calling this API on every login to update token credentials.

</details>

<details>

<summary>What happens if the frontend does not pass contactId?</summary>

Web Chat still works, but the Agent cannot identify the user or call tools with their individual permissions. Conversation history is retained only per browser (anonymously).

</details>

<details>

<summary>Are the contact and conversation history deleted after logout?</summary>

No. [Deleting credentials on logout](#logout-revoke) removes only tool credentials. The contact, conversation history, and custom attributes remain. Calling this API on the next login restores full functionality.

</details>

<details>

<summary>How do a contact's custom attributes (metadata) differ from MCP credentials?</summary>

MCP credentials are the keys to tools—they allow the AI to access enterprise system APIs as the user. Custom attributes provide background information about the user, letting the AI know who is asking and give more personalized responses. They are configured through different APIs and do not affect each other.

</details>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

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

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.maiagent.ai/tech/en/serve/contacts/contact-credentials-sync.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
