> 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/authorization-integration/contact-credentials-sync.md).

# Contact Identity Sync and Token Update

After embedding Web Chat (including [MaiGPT mode](/tech/en/api-integration/web-chat-sdk/web-chat-maigpt-mode.md)) into their product, enterprise systems need to let the AI know "who is asking": so the AI can trace back the user's conversation history and access the enterprise system's API with **that user's permissions** when calling MCP tools.

The `setup-contact-credentials` API accomplishes two things in a single call:

1. **Create or update** the mapping between "enterprise system account ↔ MaiAgent Contact"
2. Write the user's Access Token to **Contact MCP Credentials**, enabling the AI assistant to call tools on the user's behalf

{% hint style="info" %}
For the concept of Contact and the overall integration flow, refer to [Contact Introduction and Integration](/tech/en/authorization-integration/contacts.md) first. If you do not use MCP tools and only need to create contact mappings, you can also use the `POST /api/v1/contacts/` flow on that page; the API on this page is suited for scenarios where "Token sync is needed at login time."
{% endhint %}

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

This API also has a frontend version: the embed SDK's `auth` configuration (see [Web Chat Embedding and SDK](/tech/en/api-integration/web-chat-sdk.md#identify-users)) internally has the chat widget automatically call the same API. Both paths create the same set of contacts (the same `sourceId` maps to the same person); the only difference is who makes the call:

|                       | Frontend auth (SDK automatic)                                                                   | Backend integration (this page)                                            |
| --------------------- | ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| Caller                | User's browser                                                                                  | Your backend server                                                        |
| Integration cost      | Low: add an `auth` object to the config                                                         | Medium: add one API call to the login flow + store `contactId`             |
| Token confidentiality | `mcpCredentials` appears on the frontend page; users can see their own token in the source code | Token never passes through the frontend                                    |
| Best for              | Only need to identify users and preserve cross-device conversations                             | Need to bind MCP credentials so the AI can call APIs with user permissions |

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

| Scenario                         | Approach                                                                                                                                                                                                         |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Creating a new user**          | Call this API during the account creation flow and store the returned `contactId` in the member data table                                                                                                       |
| **Each login**                   | After login succeeds and an Access Token is generated, call this API before returning the result to the frontend, passing in the **new Token** (the same `sourceId` will auto-update; no duplicates are created) |
| **Backfilling existing systems** | Call this API for each existing user to backfill contact mappings; see [Batch Backfill for Existing Users](#backfill)                                                                                            |
| **Token expiry / refresh**       | Simply call this API again with the new Token                                                                                                                                                                    |
| **Logout**                       | Call the SDK's `signOut()` on the frontend to return to anonymous; on the backend, delete the contact's tool credentials — 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 WebChat 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 the API domain of your environment.
{% endhint %}

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

```json
{
  "sourceId": "user-12345",
  "name": "王小明",
  "mcpCredentials": {
    "toolId": "your-mcp-tool-id",
    "headers": {
      "Authorization": "Bearer <<User's Access Token>>"
    }
  }
}
```

| Field                    | Required | Description                                                                                                                                                                                                                                                             |
| ------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sourceId`               | ✅        | The unique identifier of the user in the enterprise system. **Repeated calls with the same `sourceId` will update rather than create duplicates.** Use non-guessable values (e.g., UUID); see the security note below                                                   |
| `name`                   |          | User display name, shown in the contact list in the MaiAgent dashboard; defaults to `Anonymous` if not provided. **Only takes effect when the contact is first created**; to update the name later, use [Sync Contact Profile](#sync-contact-profile)                   |
| `mcpCredentials.toolId`  |          | The **MCP tool** ID to bind credentials to (omit the entire `mcpCredentials` object if not using MCP tools). Only MCP-type tools are accepted; passing an API tool ID will return 400. For API tool credentials, see [Two Types of Tool Credentials](#credential-types) |
| `mcpCredentials.headers` |          | HTTP headers to send to the MCP Server, enabling the AI to call tools as the user                                                                                                                                                                                       |
| `embedOrigin`            |          | The Origin of the embedding page. When the WebChat has "allowed embed domains" configured, **server-to-server** calls (which lack a browser Origin header) need this field to pass validation                                                                           |

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

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

The returned `contactId` must be **stored in the enterprise system's member data**, and passed in when initializing Web Chat on the frontend.

{% hint style="danger" %}
**Both `sourceId` and `contactId` should be treated as identity credentials**: This API is a public endpoint — anyone who knows the `webChatId` and a user's `sourceId` can retrieve their `contactId`; and with the `contactId`, they can converse as that user and view their conversation history. Therefore:

* Use **non-guessable, non-enumerable** values for `sourceId` (e.g., UUID) — do not use sequential IDs, emails, or phone numbers
* Only output `contactId` to the user themselves on **authenticated** pages; do not include it in public page source code
* Configure the **Allowed Embed Origins** list for the Web Chat (provide the domain list to your MaiAgent integration contact) to restrict the origins that can call this API (see [Web Chat Embed & SDK](/tech/en/api-integration/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": "王小明",
    "mcpCredentials": {
      "toolId": "your-mcp-tool-id",
      "headers": {
        "Authorization": "Bearer eyJhbGciOiJIUzI1NiIs..."
      }
    }
  }'
```

## 3. Login Flow Integration Point <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: Login
    FE->>BE: Username & Password
    BE->>BE: Validation succeeds, 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 data table
    end
    BE-->>FE: Login result (includes contactId)
    FE->>MA: Load Web Chat (config includes contactId)
    MA-->>U: AI assistant serves with user identity
```

### Subsequent Login / 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: Login
    BE->>BE: Generate new Access Token
    BE->>MA: POST setup-contact-credentials<br/>(same sourceId + new Token)
    Note over MA: Recognized as the same contact<br/>Only updates credentials, no duplicate created
    MA-->>BE: contactId (same value)
    BE-->>U: Login result (reuse existing contactId)
```

Add a new field to the member data table:

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

{% hint style="success" %}
**Token expiry handling:** When a Token expires or is refreshed, simply call the same API again with the new Token. The same `sourceId` will automatically update the existing contact's credentials without creating duplicates.
{% endhint %}

{% hint style="warning" %}
**Failures should not block login:** If this API call fails, it should not prevent the user from logging into the enterprise system. Make the call asynchronous, log failures, and retry on the next login.
{% endhint %}

## 4. Batch Backfill for Existing Users <a href="#backfill" id="backfill"></a>

{% hint style="success" %}
**For large-scale backfills, use batch import instead**: Both the dashboard and the API support importing via Excel in a single operation (up to 10,000 rows), with support for query\_metadata and API tool credentials. See [Contact Batch Import](/tech/en/authorization-integration/contacts.md#bulk-import). The per-record approach below is suited for gradual backfilling through the login flow.
{% endhint %}

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

```mermaid
flowchart TD
    A[Retrieve existing user list] --> B["POST setup-contact-credentials<br/>(sourceId + name, without mcpCredentials for now)"]
    B --> C[Write contactId back to the member data table]
    C --> D{More records?}
    D -->|Yes| E["Throttle the pace<br/>(30 requests per IP per minute)"]
    E --> B
    D -->|No| F[Backfill complete]
    F -.-> G["Each user's Token credentials<br/>will be added via the login flow<br/>on their next login"]
```

* Pass each user's `sourceId` and `name`, and write the returned `contactId` back to the member data table
* `sourceId` is idempotent: the backfill script can be safely re-run without creating duplicate contacts
* If the user's Access Token is not available at the time of backfill, omit `mcpCredentials` and let the login flow add the credentials when the user next logs in
* Mind the rate limit (30 requests per IP per minute); throttle accordingly for large-scale backfills

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

To sync user information such as department, role, and service tier to MaiAgent (the AI assistant will automatically read this information and 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": "王小明",
  "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 only updates the fields provided — fields not included will not be cleared
* The `metadata` field itself is a **full replacement**: the provided array will completely overwrite the old one, so include the full metadata array
* You can call this on every login (to keep data current) or only when the user modifies their profile

<details>

<summary>Why does identity sync not require authentication while profile sync requires an API Key?</summary>

`setup-contact-credentials` is a public endpoint that only returns a UUID. Even if called maliciously, the worst outcome is writing incorrect credentials — the enterprise system's own validation mechanisms will reject invalid Tokens.

However, contact profile data (name, department, attributes) is read by the AI and incorporated into its responses. If this were a public endpoint, a malicious actor could change the name to arbitrary content and feed it directly to the LLM. Therefore, contact profile updates require **API Key authentication** to ensure only authorized backends can make modifications.

</details>

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

There are two things to do at logout — frontend and backend — each with a different scope:

|                  | Frontend `MaiAgent.auth.signOut()`                                                                    | Backend credential deletion (this section)                                        |
| ---------------- | ----------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| Effect           | Chat window returns to anonymous state, clears `contextData`                                          | Removes the contact's MCP/API tool credentials from MaiAgent                      |
| Tool credentials | Does **not** touch already-written 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/api-integration/web-chat-sdk.md#api-auth)) | Called by your backend during the logout flow                                     |

If your system already revokes the Access Token on your own auth server at logout, the credentials stored by MaiAgent are effectively invalidated. Deleting credentials on the backend is a defense-in-depth measure — it is recommended to do both, to avoid stale Tokens lingering 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: Logout
    FE->>MA: MaiAgent.auth.signOut()
    Note over FE,MA: Chat window returns to anonymous state
    FE->>BE: Logout request
    BE->>BE: Revoke own 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 preserved<br/>Rebuilt via identity sync API on next login
```

### Delete Credentials 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 is `Authorization: Api-Key <<Your API Key>>` (same as the Contact API). A successful response 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>>'
```

After deletion, the contact itself, conversation history, and custom attributes are all preserved. When the user logs in again, the login flow's call to `setup-contact-credentials` will recreate the credentials (the same contact-tool combination is updated/recreated, not duplicated).

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

`setup-contact-credentials` only returns a `contactId` and does not include the credential ID. To obtain the `credentialId`:

* `GET /api/v1/contacts/{contactId}/` (`Api-Key` authentication) — the response includes `mcpCredentials` and `apiCredentials` arrays, each entry containing `id` and `tool` information. Find the target credential's `id` by matching `tool.id`
* When [creating credentials via the Contact API](#credential-types), the response is the full contact object — you can store the credential `id` at creation time

The credential `headers` in the response may appear in masked form depending on the caller's identity; the revocation flow only needs the `id` and is not affected.

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

Contacts can bind dedicated credentials for two types of tools, with different configuration entry points:

|                      | MCP Tool Credentials                                                                                             | API Tool Credentials                                                                                                    |
| -------------------- | ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| Purpose              | AI calls MCP Server as the user                                                                                  | AI calls API tools as the user                                                                                          |
| Sync at login        | ✅ This page's `setup-contact-credentials` (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 single | `PATCH`/`DELETE /api/v1/contacts/{contactId}/mcp-credentials/{credentialId}/`                                    | `PATCH`/`DELETE /api/v1/contacts/{contactId}/api-credentials/{credentialId}/`                                           |
| Batch import         | ✖                                                                                                                | ✅ The `API Credentials` column in Excel; see [Batch Import](/tech/en/authorization-integration/contacts.md#bulk-import) |
| Dashboard UI         | Contact edit → MCP Credentials (see [User Guide](https://docs.maiagent.ai/tools/setup-contacts-mcp-credentials)) | —                                                                                                                       |

Both credential endpoints of the Contact API require `Api-Key` authentication, and the body format is the same:

```json
{
  "tool": "<<Tool ID>>",
  "headers": { "Authorization": "Bearer <<User's Token>>" }
}
```

Repeated calls for the same (contact, tool) combination will update the existing record, not create duplicates. The update/delete endpoints for individual records use the `credentialId` to identify the target credential; see [Retrieving the credentialId](#get-credential-id) for how to obtain it.

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

| HTTP Status Code | Possible Cause                                                                                                                    | Recommended Action                                            |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| `400`            | Required field missing (e.g., `sourceId`); Origin not in the allowed embed domain list; `toolId` does not exist or is unavailable | Check the request body and WebChat settings                   |
| `404`            | WebChat ID does not exist                                                                                                         | Verify the `webChatId` is correct                             |
| `429`            | Rate limit exceeded (30 requests per IP per minute)                                                                               | Reduce call frequency and retry                               |
| `500`            | Server error                                                                                                                      | Retry later; if the issue persists, contact the MaiAgent team |

## 9. FAQ <a href="#faq" id="faq"></a>

<details>

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

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

</details>

<details>

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

The `contactId` does not change once created and can be persistently stored in the member data table. However, it is still recommended to call this API on each login to update the Token credentials.

</details>

<details>

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

Web Chat will still function, but the AI assistant cannot identify the user, cannot call tools with individual permissions, and conversation history will only be retained per browser (anonymous).

</details>

<details>

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

No. [Deleting credentials at logout](#logout-revoke) only removes the tool credentials; the contact itself, conversation history, and custom attributes are all preserved. Full functionality is restored when the user logs in again and this API is called.

</details>

<details>

<summary>What is the difference between contact custom attributes (metadata) and MCP credentials?</summary>

MCP credentials are "keys for tools" — they enable the AI to access the enterprise system's API as the user. Custom attributes are "user background information" — they let the AI know who is asking and provide more personalized responses. The two 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 dynamically by asking a question.

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

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
