---
title: "Se connecter avec Matchai"
description: "Laissez un professionnel connecter son compte Matchai à votre application avec OAuth 2.1 et PKCE. Points de terminaison, autorisation, jetons, code TypeScript."
last_updated: "2026-10-21"
lang: "fr-CA"
status: "coming-soon"
canonical: "https://matchai.co/docs/fr/developers/authentication/connect-with-matchai"
---

# Se connecter avec Matchai

> Laissez un professionnel connecter son compte Matchai à votre application avec OAuth 2.1 et PKCE. Points de terminaison, autorisation, jetons, code TypeScript.

**Bientôt disponible. Cette fonctionnalité est documentée avant son lancement pour que vous puissiez planifier votre intégration; elle n’est pas encore offerte.**

Source: https://matchai.co/docs/fr/developers/authentication/connect-with-matchai

**Se connecter avec Matchai** est la façon dont une application obtient l'accès au compte d'un professionnel, avec sa permission. C'est OAuth 2.1 avec le flux de code d'autorisation et PKCE. Le professionnel voit ce que votre application peut faire et qui paie, appuie sur **Autoriser**, et votre application apparaît sur sa page Connecteurs, où il peut la déconnecter en tout temps.

Utilisez-le pour tout ce que d'autres professionnels utiliseront : un créateur de sites Web, un CRM, l'application d'un partenaire, un assistant IA. Pour votre propre compte, une [clé d'API](https://matchai.co/docs/fr/developers/authentication/api-keys) est plus simple.

Chaque connexion est **un compte Matchai connecté à une application**, avec ses propres jetons. Un créateur de sites Web qui a trois courtiers comme clients détient trois connexions.

## Avant de commencer [#before-you-start]

- [Créez une application Matchai](https://matchai.co/docs/fr/developers/authentication/create-a-matchai-app) pour obtenir un identifiant client et un secret client, et pour enregistrer vos URL de redirection.
- Décidez de quelles [portées](https://matchai.co/docs/fr/developers/authentication#scopes) votre application a besoin. Demandez la liste la plus courte qui fonctionne.
- Ayez un serveur capable de garder le secret client confidentiel. L'échange de jetons se fait là, jamais dans le navigateur.

## Points de terminaison [#endpoints]

| Objet | URL |
|---|---|
| Autorisation (la page où l'on appuie sur **Autoriser**) | `https://matchai.co/oauth/authorize` |
| Jeton et actualisation | `https://api.matchai.co/oauth/token` |
| Enregistrement dynamique des clients | `https://api.matchai.co/oauth/register` |
| Métadonnées du serveur d'autorisation | `https://api.matchai.co/.well-known/oauth-authorization-server` |
| Métadonnées de la ressource protégée | `https://api.matchai.co/.well-known/oauth-protected-resource` pour l'API REST, et `https://matchai.co/.well-known/oauth-protected-resource` pour le serveur MCP |

Les trois derniers servent aux clients MCP et aux bibliothèques qui découvrent les points de terminaison par elles-mêmes. Si vous écrivez le flux à la main, vous n'avez besoin que des deux premiers.

PKCE avec `S256` est obligatoire. Les codes d'autorisation sont à usage unique et de courte durée, et les jetons d'accès sont de courte durée : utilisez le jeton d'actualisation pour en obtenir de nouveaux.

## Le flux [#the-flow]

<Steps>
<Step>

**Préparez PKCE et une valeur d'état.** Générez un `code_verifier` aléatoire et conservez-le. Envoyez son hachage SHA-256, encodé en base64url, comme `code_challenge`. Générez aussi un `state` aléatoire. Conservez les deux à un endroit que seul ce navigateur peut renvoyer, comme un témoin (cookie) `HttpOnly`.

</Step>
<Step>

**Redirigez le professionnel vers Matchai.** Envoyez son navigateur vers l'URL d'autorisation avec ces paramètres :

<TypeTable
  type={{
    response_type: { type: '"code"', required: true, description: 'Toujours code.' },
    client_id: { type: 'string', required: true, description: "L'identifiant client de votre application Matchai." },
    redirect_uri: { type: 'string', required: true, description: "L'une des URL de redirection que vous avez enregistrées, écrite exactement de la même façon." },
    scope: { type: 'string', required: true, description: 'Les portées que vous demandez, séparées par des espaces, par exemple "profile:read listings:read contacts:write".' },
    state: { type: 'string', required: true, description: "Une valeur impossible à deviner. Matchai la renvoie telle quelle, et vous devez vérifier qu'elle correspond." },
    code_challenge: { type: 'string', required: true, description: 'Le hachage SHA-256 de votre code_verifier, encodé en base64url.' },
    code_challenge_method: { type: '"S256"', required: true, description: 'Toujours S256.' },
  }}
/>

```text title="Authorization URL"
https://matchai.co/oauth/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https%3A%2F%2Fapp.your-website-company.example%2Fmatchai%2Fcallback
  &scope=profile%3Aread%20listings%3Aread%20contacts%3Awrite
  &state=3b1f0c9e7a2d4e68
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256
```

</Step>
<Step>

**Le professionnel répond.** S'il n'est pas connecté à Matchai, il se connecte d'abord. Il voit ensuite la [page d'autorisation](#the-allow-page) et appuie sur **Autoriser**, ou refuse.

</Step>
<Step>

**Matchai le renvoie vers vous.** Il revient à votre `redirect_uri` avec un `code` et votre `state` :

```text title="Callback"
https://app.your-website-company.example/matchai/callback?code=…&state=3b1f0c9e7a2d4e68
```

S'il refuse, il y a un `error=access_denied` et votre `state` au lieu d'un `code`. Avant toute autre chose, vérifiez que `state` correspond à la valeur que vous avez conservée.

</Step>
<Step>

**Échangez le code contre des jetons.** Depuis votre serveur, faites un `POST` vers le point de terminaison de jeton avec un corps encodé comme un formulaire :

```bash title="Terminal"
curl https://api.matchai.co/oauth/token \
  -d grant_type=authorization_code \
  -d code="$MATCHAI_AUTH_CODE" \
  -d redirect_uri="https://app.your-website-company.example/matchai/callback" \
  -d client_id="$MATCHAI_CLIENT_ID" \
  -d client_secret="$MATCHAI_CLIENT_SECRET" \
  -d code_verifier="$MATCHAI_CODE_VERIFIER"
```

```json title="Response"
{
  "access_token": "…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "…",
  "scope": "profile:read listings:read contacts:write"
}
```

`expires_in` est la durée de vie du jeton d'accès, en secondes. Lisez-la dans chaque réponse; ne codez pas de durée de vie en dur. Conservez les deux jetons pour ce seul professionnel, chiffrés, et ne les journalisez jamais.

</Step>
<Step>

**Appelez l'API.** Envoyez le jeton d'accès comme jeton de porteur. Commencez par `GET /v1/me` : il vous indique quel compte vient de se connecter. `GET /v1/me/connection` vous indique si les actions finales demandent d'abord.

```bash title="Terminal"
curl https://api.matchai.co/v1/me \
  -H "Authorization: Bearer $MATCHAI_ACCESS_TOKEN"
```

</Step>
<Step>

**Actualisez le jeton quand il expire.** Un jeton d'accès expiré reçoit la réponse `401` avec le code `token_expired`. Échangez le jeton d'actualisation contre un nouveau jeton d'accès, puis répétez la requête une fois :

```bash title="Terminal"
curl https://api.matchai.co/oauth/token \
  -d grant_type=refresh_token \
  -d refresh_token="$MATCHAI_REFRESH_TOKEN" \
  -d client_id="$MATCHAI_CLIENT_ID" \
  -d client_secret="$MATCHAI_CLIENT_SECRET"
```

Conservez toujours le jeton d'actualisation le plus récent qu'une réponse vous donne. Si l'actualisation échoue, la connexion a pris fin : demandez au professionnel de reconnecter son compte.

</Step>
</Steps>

## La page d'autorisation [#the-allow-page]

La page d'autorisation est le moment où le professionnel décide. Elle est conçue pour qu'il n'ait jamais à deviner :

| Ce qu'elle montre | D'où ça vient |
|---|---|
| Le **nom, le logo et le site Web** de votre application, avec une marque quand Matchai a vérifié l'éditeur | Votre [application Matchai](https://matchai.co/docs/fr/developers/authentication/create-a-matchai-app) |
| **Ce que votre application peut faire**, chaque portée en mots simples, comme « Voir vos inscriptions » ou « Ajouter et modifier vos contacts » | Le `scope` que vous avez demandé |
| Si les **actions finales demandent d'abord** | Votre connexion commence sur **Me demander**. L'envoi, la publication, le paiement et la suppression attendent son approbation. |
| **Qui paie** | « Inclus avec » votre produit quand votre application est payée par le partenaire, une ligne indiquant que la connexion s'ajoute à sa facture Matchai quand elle est payée par l'utilisateur, et rien du tout en [mode test](https://matchai.co/docs/fr/developers/authentication/create-a-matchai-app#test-mode) |

Appuyer sur **Autoriser** crée la connexion. À partir de ce moment, elle apparaît sur sa page Connecteurs avec les mêmes détails, et il peut changer la façon dont les actions finales sont gérées, ou appuyer sur **Déconnecter**.

<Callout title="Pourquoi les portées se lisent comme des phrases">
Le professionnel décide s'il laisse le logiciel d'un inconnu agir en son nom. Une liste comme `contacts:write messages:send` l'oblige à traduire; une phrase lui permet de décider. Demandez les portées que vous pouvez expliquer en une ligne chacune.
</Callout>

## Exemple : un créateur de sites Web connecte une courtière [#example-a-website-builder-connects-a-broker]

Disons que vous exploitez une entreprise qui crée des sites Web pour des courtiers. Votre produit s'appelle « Votre entreprise de sites Web » dans Matchai, et Camille Tremblay, une courtière, est l'une de vos clientes.

<Steps>
<Step>

Camille ouvre les réglages de son site Web dans votre produit et appuie sur **Connecter Matchai**. Votre serveur exécute les étapes 1 et 2 ci-dessus et la redirige.

</Step>
<Step>

Elle est déjà connectée à Matchai. La page d'autorisation nomme Votre entreprise de sites Web, énumère ce que l'application peut faire (voir ses inscriptions, ajouter des contacts, être avisée des nouveaux prospects) et indique qui paie. Elle appuie sur **Autoriser**.

</Step>
<Step>

Elle revient dans votre produit. Votre serveur échange le code, appelle `GET /v1/me` et conserve ses jetons dans son dossier client. Cela lui a pris quelques clics, et aucun mot de passe n'a été partagé.

</Step>
<Step>

Votre produit lit ses inscriptions avec `GET /v1/listings` et les affiche sur son nouveau site Web. Il s'abonne à `listing.published`, `listing.updated` et `lead.created` avec `POST /v1/webhook-subscriptions`, pour que le site reste à jour sans interrogation périodique.

</Step>
<Step>

Un visiteur remplit le formulaire de contact de son site. Votre serveur l'envoie avec `POST /v1/enquiries`. Un prospect et sa conversation apparaissent dans les contacts Matchai de Camille, et `lead.created` arrive à votre point de terminaison.

</Step>
<Step>

Des mois plus tard, Camille déconnecte votre produit à partir de sa page Connecteurs. Votre appel suivant reçoit la réponse `401`, ses abonnements aux webhooks sont désactivés, et votre produit affiche « Reconnecter Matchai ». Rien n'a été supprimé dans son compte Matchai.

</Step>
</Steps>

Votre client suivant, un deuxième courtier, suit le même flux et obtient un ensemble de jetons distinct. Aucune des deux connexions ne peut voir les données de l'autre courtier.

## Un exemple complet [#a-complete-example]

Ce serveur effectue les étapes 1 à 5 pour un client connecté. Il utilise Express. `currentCustomerId` et `saveConnection` représentent votre propre système de connexion et votre propre stockage.

```ts title="server.ts"
import { createHash, randomBytes } from 'node:crypto';
import cookieParser from 'cookie-parser';
import express from 'express';

// Your own code: who is signed in to your product, and where tokens are kept.
declare function currentCustomerId(request: express.Request): string;
declare function saveConnection(connection: {
  customerId: string;
  matchaiUserId: string;
  accessToken: string;
  refreshToken: string;
  expiresAt: Date;
}): Promise<void>;

const clientId = process.env.MATCHAI_CLIENT_ID!;
const clientSecret = process.env.MATCHAI_CLIENT_SECRET!;
const redirectUri = 'https://app.your-website-company.example/matchai/callback';
const scopes = ['profile:read', 'listings:read', 'contacts:write', 'webhooks:manage'];

const base64url = (bytes: Buffer) => bytes.toString('base64url');

const app = express();
app.use(cookieParser());

// Steps 1 and 2: send the professional to Matchai.
app.get('/matchai/connect', (request, response) => {
  currentCustomerId(request); // Only a signed-in customer can start a connection.

  const state = base64url(randomBytes(24));
  const verifier = base64url(randomBytes(48));
  const challenge = base64url(createHash('sha256').update(verifier).digest());

  // Keep both values in a cookie that only this browser sends back.
  response.cookie('matchai_oauth', JSON.stringify({ state, verifier }), {
    httpOnly: true,
    secure: true,
    sameSite: 'lax',
    maxAge: 10 * 60 * 1000,
  });

  const authorize = new URL('https://matchai.co/oauth/authorize');
  authorize.search = new URLSearchParams({
    response_type: 'code',
    client_id: clientId,
    redirect_uri: redirectUri,
    scope: scopes.join(' '),
    state,
    code_challenge: challenge,
    code_challenge_method: 'S256',
  }).toString();

  response.redirect(authorize.toString());
});

// Steps 4 and 5: Matchai sends the professional back here.
app.get('/matchai/callback', async (request, response) => {
  const customerId = currentCustomerId(request);
  const saved = JSON.parse(request.cookies.matchai_oauth ?? 'null') as
    | { state: string; verifier: string }
    | null;
  response.clearCookie('matchai_oauth');

  if (request.query.error === 'access_denied') {
    return response.redirect('/settings/website?matchai=declined');
  }

  const { code, state } = request.query;
  if (!saved || typeof code !== 'string' || state !== saved.state) {
    return response.status(400).send('This connection request is not valid. Please start again.');
  }

  const tokenResponse = await fetch('https://api.matchai.co/oauth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      code,
      redirect_uri: redirectUri,
      client_id: clientId,
      client_secret: clientSecret,
      code_verifier: saved.verifier,
    }),
  });
  if (!tokenResponse.ok) {
    return response.status(502).send('Matchai did not accept the connection. Please try again.');
  }
  const tokens = await tokenResponse.json();

  // Learn which Matchai account just connected.
  const me = await fetch('https://api.matchai.co/v1/me', {
    headers: { Authorization: `Bearer ${tokens.access_token}` },
  }).then((result) => result.json());

  await saveConnection({
    customerId,
    matchaiUserId: me.user_id,
    accessToken: tokens.access_token,
    refreshToken: tokens.refresh_token,
    expiresAt: new Date(Date.now() + tokens.expires_in * 1000),
  });

  response.redirect('/settings/website?matchai=connected');
});

app.listen(3000);
```

Ensuite, chaque appel à l'API passe par une seule fonction qui actualise un jeton expiré et détecte une connexion qui a pris fin :

```ts title="matchai-client.ts"
type Tokens = { accessToken: string; refreshToken: string; expiresAt: Date };

// Your own code: tokens are stored per customer.
declare function loadTokens(customerId: string): Promise<Tokens>;
declare function saveTokens(customerId: string, tokens: Tokens): Promise<void>;
declare function markDisconnected(customerId: string): Promise<void>;

async function refresh(refreshToken: string): Promise<Tokens | null> {
  const response = await fetch('https://api.matchai.co/oauth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'refresh_token',
      refresh_token: refreshToken,
      client_id: process.env.MATCHAI_CLIENT_ID!,
      client_secret: process.env.MATCHAI_CLIENT_SECRET!,
    }),
  });
  if (!response.ok) return null;

  const tokens = await response.json();
  return {
    accessToken: tokens.access_token,
    // Keep the newest refresh token you were given.
    refreshToken: tokens.refresh_token ?? refreshToken,
    expiresAt: new Date(Date.now() + tokens.expires_in * 1000),
  };
}

export async function matchaiFetch(
  customerId: string,
  path: string,
  init: RequestInit & { headers?: Record<string, string> } = {},
): Promise<Response> {
  let tokens = await loadTokens(customerId);

  const call = () =>
    fetch(`https://api.matchai.co/v1${path}`, {
      ...init,
      headers: { ...init.headers, Authorization: `Bearer ${tokens.accessToken}` },
    });

  const response = await call();
  if (response.status !== 401) return response;

  // The access token expired, or the professional disconnected. Refresh once.
  const refreshed = await refresh(tokens.refreshToken);
  if (!refreshed) {
    await markDisconnected(customerId); // Show "Reconnect Matchai" in your product.
    return response;
  }
  await saveTokens(customerId, refreshed);
  tokens = refreshed;
  return call();
}
```

Actualisez les jetons d'un seul client à la fois. Si deux requêtes actualisent en même temps, seule la première peut utiliser le jeton d'actualisation qu'elle détient.

## Clients MCP et enregistrement dynamique des clients [#mcp-clients-and-dynamic-client-registration]

Les assistants IA qui parlent MCP, comme Claude et ChatGPT, ne vous demandent pas d'enregistrer une application. Ils trouvent la connexion par eux-mêmes et s'enregistrent avec l'**enregistrement dynamique des clients** :

1. Le client appelle un outil propre à l'utilisateur sur `https://matchai.co/mcp` sans jeton. Le serveur répond `401` avec un en-tête `WWW-Authenticate: Bearer resource_metadata=…` qui pointe vers les métadonnées de la ressource protégée.
2. Le client lit les métadonnées, trouve le serveur d'autorisation et lit `https://api.matchai.co/.well-known/oauth-authorization-server`.
3. Il s'enregistre à `https://api.matchai.co/oauth/register` et obtient un identifiant client.
4. Il exécute le même flux que ci-dessus. Le professionnel voit la même page d'autorisation, qui nomme l'assistant.

Pour ajouter Matchai à un assistant, il suffit d'entrer l'adresse du serveur. Le [guide du serveur MCP](https://matchai.co/docs/fr/developers/mcp-server) couvre les outils publics, qui continuent de fonctionner sans connexion.

## Mettre fin à une connexion [#ending-a-connection]

- **Le professionnel se déconnecte.** Il appuie sur **Déconnecter** sur la page Connecteurs. Chaque jeton de la connexion cesse de fonctionner, ses abonnements aux webhooks sont désactivés (`disabled_reason: connection_ended`), et rien n'est supprimé dans son compte.
- **Votre application se déconnecte.** Appelez `DELETE /v1/me/connection` ([`disconnect`](https://matchai.co/docs/developers/api-reference/me/disconnect)) pour obtenir le même effet, par exemple quand un client quitte votre produit.

Dans les deux cas, supprimez les jetons conservés et traitez les réponses `401` qui suivent comme un signal de reconnexion.

## Liste de vérification de sécurité [#security-checklist]

- Vérifiez `state` à chaque rappel, et rejetez un rappel que vous n'avez pas lancé.
- Utilisez PKCE avec `S256` dans chaque flux. Matchai refuse un flux qui ne l'utilise pas.
- Gardez le secret client et chaque jeton sur votre serveur, chiffrés au repos et hors de vos journaux.
- Enregistrez des URL de redirection exactes, et n'en construisez jamais une à partir d'une saisie de l'utilisateur.
- Demandez le moins de portées possible. En demander d'autres plus tard veut dire redemander l'accord du professionnel.
- Gardez un ensemble de jetons par professionnel, et n'utilisez jamais le jeton d'un client pour la requête d'un autre.

<Cards>
  <Card title="Créer une application Matchai" href="https://matchai.co/docs/fr/developers/authentication/create-a-matchai-app">
    Obtenez un identifiant client et un secret, et configurez le mode test.
  </Card>
  <Card title="Authentification" href="https://matchai.co/docs/fr/developers/authentication">
    Les portées, les actions finales et les limites d'énergie.
  </Card>
  <Card title="Erreurs" href="https://matchai.co/docs/fr/developers/errors">
    Ce que signifie un 401 ou un 403, et quoi faire ensuite.
  </Card>
  <Card title="Webhooks" href="https://matchai.co/docs/fr/developers/webhooks">
    Abonnez-vous aux événements de chaque compte connecté.
  </Card>
</Cards>

## Sitemap

- [Toutes les pages de la documentation Matchai](https://matchai.co/docs/sitemap.md)
- [llms.txt](https://matchai.co/docs/fr/llms.txt)
