# API contacts

Un contact est une personne qui peut recevoir tes e-mails. On le retrouve par son `id` ou par son adresse e-mail : `/contacts/lea@exemple.ch` marche aussi bien que `/contacts/kn4d7b2x9q1m6v3r8c0w5h2t`. Encode les caractères spéciaux de l’adresse, comme `+` (`%2B`).

## Forme d’un contact

```json
{
  "object": "contact",
  "id": "kn4d7b2x9q1m6v3r8c0w5h2t",
  "email": "lea@exemple.ch",
  "first_name": "Léa",
  "last_name": "Martin",
  "unsubscribed": false,
  "status": "subscribed",
  "properties": { "entreprise": "Atelier Nord" },
  "created_at": "2026-10-11T08:00:00.000Z"
}
```

| Champ                     | Type             | Description                                                                                                 |
| ------------------------- | ---------------- | ----------------------------------------------------------------------------------------------------------- |
| `id`                      | string           | Identifiant du contact.                                                                                     |
| `email`                   | string           | Adresse du contact. Elle identifie le contact et ne se modifie pas.                                         |
| `first_name`, `last_name` | string ou `null` | Prénom et nom. `null` quand ils ne sont pas renseignés.                                                     |
| `unsubscribed`            | boolean          | `true` dès que le statut n’est pas `subscribed`.                                                            |
| `status`                  | string           | `subscribed`, `unsubscribed`, `bounced` (adresse qui rebondit) ou `complained` (signalé comme indésirable). |
| `properties`              | objet            | Les champs libres du contact, sous forme de texte.                                                          |
| `created_at`              | string           | Date de création, en ISO 8601.                                                                              |

Les propriétés s’utilisent comme variables dans les modèles : `{{entreprise}}`. Un contact accepte 50 propriétés au plus, chacune avec un nom de 64 caractères au plus et une valeur de 500 caractères au plus. Les nombres et les booléens sont enregistrés comme texte.

## POST `/contacts`

Ajoute un contact, ou le met à jour.

Si l’adresse existe déjà dans ton espace, le contact est mis à jour au lieu d’être dupliqué : rejouer l’appel est sans risque. Les propriétés envoyées s’ajoutent à celles qui existent.

| Champ          | Type             | Obligatoire | Description                                                               |
| -------------- | ---------------- | ----------- | ------------------------------------------------------------------------- |
| `email`        | string           | Oui         | Adresse valide, 320 caractères au plus.                                   |
| `first_name`   | string ou `null` | Non         | Prénom, 100 caractères au plus.                                           |
| `last_name`    | string ou `null` | Non         | Nom, 100 caractères au plus.                                              |
| `unsubscribed` | boolean          | Non         | `true` pour enregistrer le contact comme désabonné.                       |
| `properties`   | objet            | Non         | Champs libres, texte à texte, 50 au plus.                                 |
| `list_ids`     | string\[]        | Non         | Listes où ajouter le contact, 20 au plus. Une liste inconnue donne `422`. |

```bash
curl -X POST https://api.mail.hgdc.ch/contacts \
  -H "Authorization: Bearer $AGENTMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "lea@exemple.ch",
    "first_name": "Léa",
    "last_name": "Martin",
    "properties": { "entreprise": "Atelier Nord" },
    "list_ids": ["jd7q4v2m8x1r5n9c3b6z0h4w"]
  }'
```

```ts
const response = await fetch("https://api.mail.hgdc.ch/contacts", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.AGENTMAIL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    email: "lea@exemple.ch",
    first_name: "Léa",
    last_name: "Martin",
    properties: { entreprise: "Atelier Nord" },
    list_ids: ["jd7q4v2m8x1r5n9c3b6z0h4w"],
  }),
})
const contact = await response.json()
```

```ts
import { Agentmail } from "./agentmail"

const mail = new Agentmail()
const contact = await mail.contacts.create({
  email: "lea@exemple.ch",
  first_name: "Léa",
  last_name: "Martin",
  properties: { entreprise: "Atelier Nord" },
  list_ids: ["jd7q4v2m8x1r5n9c3b6z0h4w"],
})
```

Réponse : statut `200`.

```json
{
  "object": "contact",
  "id": "kn4d7b2x9q1m6v3r8c0w5h2t"
}
```

## GET `/contacts`

Lit une page de contacts, du plus récent au plus ancien.

| Champ   | Type   | Obligatoire | Description                                                                              |
| ------- | ------ | ----------- | ---------------------------------------------------------------------------------------- |
| `limit` | nombre | Non         | Contacts par page, de 1 à 100. Défaut : 50. Au-delà de 100, la valeur est ramenée à 100. |
| `after` | string | Non         | Curseur `next_cursor` de la page précédente. Sans lui, on lit la première page.          |

```bash
curl "https://api.mail.hgdc.ch/contacts?limit=100" \
  -H "Authorization: Bearer $AGENTMAIL_API_KEY"
```

```ts
import { Agentmail } from "./agentmail"

const mail = new Agentmail()
for await (const contact of mail.contacts.listAll({ limit: 100 })) {
  console.log(contact.email)
}
```

Réponse : statut `200`.

```json
{
  "object": "list",
  "data": [
    {
      "object": "contact",
      "id": "kn4d7b2x9q1m6v3r8c0w5h2t",
      "email": "lea@exemple.ch",
      "first_name": "Léa",
      "last_name": "Martin",
      "unsubscribed": false,
      "status": "subscribed",
      "properties": { "entreprise": "Atelier Nord" },
      "created_at": "2026-10-11T08:00:00.000Z"
    }
  ],
  "has_more": true,
  "next_cursor": "eyJjdXJzb3IiOiJrbjRkN2IyeDlxMW02djNyOGMwdzVoMnQifQ"
}
```

Pour la page suivante, reprends `next_cursor` dans `after` : `GET /contacts?limit=100&after=eyJjdXJzb3IiOi…`. Sur la dernière page, `has_more` vaut `false` et `next_cursor` vaut `null`. Le SDK parcourt toutes les pages seul avec `listAll`.

## GET `/contacts/:id_ou_adresse`

Lit un contact.

| Champ           | Type   | Obligatoire         | Description                                    |
| --------------- | ------ | ------------------- | ---------------------------------------------- |
| `id_ou_adresse` | string | Oui, dans le chemin | Identifiant du contact, ou son adresse e-mail. |

```bash
curl https://api.mail.hgdc.ch/contacts/lea%40exemple.ch \
  -H "Authorization: Bearer $AGENTMAIL_API_KEY"
```

Réponse : statut `200`, avec la forme d’un contact décrite plus haut. Si le contact n’existe pas dans ton espace, la réponse est un `404` avec `name: "not_found"`.

## PATCH `/contacts/:id_ou_adresse`

Modifie un contact. N’envoie que les champs à changer : un champ absent ne change pas.

| Champ           | Type             | Obligatoire         | Description                                                                                                                                              |
| --------------- | ---------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id_ou_adresse` | string           | Oui, dans le chemin | Identifiant du contact, ou son adresse e-mail.                                                                                                           |
| `first_name`    | string ou `null` | Non                 | Nouveau prénom. `null` l’efface.                                                                                                                         |
| `last_name`     | string ou `null` | Non                 | Nouveau nom. `null` l’efface.                                                                                                                            |
| `unsubscribed`  | boolean          | Non                 | `true` désabonne le contact, `false` le réabonne. Une adresse qui a rebondi ou signalé un e-mail ne se réabonne pas à la main : la réponse est un `422`. |
| `properties`    | objet            | Non                 | Champs à ajouter ou à remplacer. Les autres sont gardés.                                                                                                 |
| `list_ids`      | string\[]        | Non                 | Listes où ajouter le contact. Il n’est retiré d’aucune autre liste.                                                                                      |

Le champ `email` n’est pas modifiable : il est ignoré s’il est envoyé.

```bash
curl -X PATCH https://api.mail.hgdc.ch/contacts/lea%40exemple.ch \
  -H "Authorization: Bearer $AGENTMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "properties": { "ville": "Lausanne" }, "unsubscribed": false }'
```

Réponse : statut `200`, avec `{ "object": "contact", "id": "…" }`.

## DELETE `/contacts/:id_ou_adresse`

Supprime un contact de ton espace.

```bash
curl -X DELETE https://api.mail.hgdc.ch/contacts/lea%40exemple.ch \
  -H "Authorization: Bearer $AGENTMAIL_API_KEY"
```

Réponse : statut `200`.

```json
{
  "object": "contact",
  "id": "kn4d7b2x9q1m6v3r8c0w5h2t",
  "deleted": true
}
```

Supprimer un contact déjà supprimé renvoie un `404` avec `name: "not_found"`.

## Si ça ne marche pas

- **`404 not_found` sur un contact qui existe** : l’adresse n’a pas été encodée. Un `+` doit devenir `%2B`, un `@` peut rester tel quel ou devenir `%40`.
- **`422` au réabonnement** : l’adresse a rebondi ou été signalée. Elle ne peut pas être réabonnée à la main.
- **Une propriété n’apparaît pas dans un modèle** : son nom doit correspondre exactement à la variable, en minuscules. Voir [Variables](/docs/reference/variables/).
