> ## Documentation Index
> Fetch the complete documentation index at: https://docs.oneclickdz.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Découvrir les Factures

> Démarrer une découverte de factures pour un compte partenaire

## Vue d'Ensemble

Demande à un facturier ce qu'un compte donné doit actuellement. La réponse est un `transactionId` que vous interrogez ensuite ; lorsque la transaction atteint `READY`, son tableau `bills[]` contient tout ce qui est payable.

<Warning>
  **`200` est un accusé de réception, pas un résultat.** Cela signifie que la requête a été acceptée et qu'une transaction a été créée. Les factures arrivent plus tard, dans la transaction. Ne traitez jamais cette réponse comme « le compte ne doit rien ».
</Warning>

La découverte ne déplace pas d'argent et ne vous engage à rien. Vous pouvez l'exécuter en toute sécurité avant de montrer à un client ce qu'il doit.

## Corps de la Requête

<ParamField body="partner" type="string" required>
  Le facturier à interroger. Exactement l'une des valeurs `ADE`, `SONELGAZ`, `SEAAL`, `AADL`, `Algérie Télécom` — accents compris.

  Consultez d'abord [Lister les Partenaires](/fr/api-reference/bill-payment/list-partners) ; un partenaire `UNAVAILABLE` répond `503 PARTNER_UNAVAILABLE`.
</ParamField>

<ParamField body="account" type="object" required>
  Le compte à rechercher. Il doit porter **exactement un** identifiant — voir [Identifiants de compte](#account-identifiers) ci-dessous. Zéro identifiant, ou deux, est rejeté.
</ParamField>

<ParamField body="ref" type="string" required>
  Votre propre référence pour cette découverte. Maximum 100 caractères, et unique parmi vos transactions en cours pour ce partenaire.

  Réutiliser une `ref` répond `403 DUPLICATED_REF`. C'est aussi ainsi que vous récupérez une réponse perdue — voir [Obtenir une Transaction par Référence](/fr/api-reference/bill-payment/check-by-ref).
</ParamField>

## Identifiants de compte

Envoyez le champ qui appartient au partenaire que vous interrogez. C'est également le champ que l'API retourne dans `account` sur chaque transaction pour ce partenaire.

| Partenaire        | Champ            | Notes                                                                                             |
| ----------------- | ---------------- | ------------------------------------------------------------------------------------------------- |
| `ADE`             | `reference`      | Référence client, jusqu'à 50 caractères                                                           |
| `SEAAL`           | `reference`      | Référence client, jusqu'à 50 caractères                                                           |
| `SONELGAZ`        | `contractNumber` | Numéro de contrat, jusqu'à 50 caractères                                                          |
| `AADL`            | `aadlNumber`     | Numéro de dossier de logement, jusqu'à 50 caractères                                              |
| `Algérie Télécom` | `phoneNumber`    | Ligne fixe algérienne, `0` ou `+213` suivi d'un chiffre de 2 à 4 et de 7 chiffres supplémentaires |

Deux formes plus riches sont également acceptées lorsque le facturier a besoin de plus d'un seul numéro pour identifier une facture :

<AccordionGroup>
  <Accordion title="sonelgaz — forme facture">
    Les trois champs sont requis ensemble.

    ```json theme={null}
    {
      "partner": "SONELGAZ",
      "account": {
        "sonelgaz": {
          "invoice_number": "9876543210",
          "amount_without_stamp": "15000",
          "ebb_key": "ABC123"
        }
      },
      "ref": "disc-inv-2026-0042"
    }
    ```

    `invoice_number` jusqu'à 20 caractères, `amount_without_stamp` jusqu'à 20, `ebb_key` jusqu'à 30.
  </Accordion>

  <Accordion title="ade — forme facture">
    Les quatre champs sont requis ensemble.

    ```json theme={null}
    {
      "partner": "ADE",
      "account": {
        "ade": {
          "sub_id": "000123456789",
          "period": "07/2026",
          "amount": "12000",
          "pay_key": "1234567"
        }
      },
      "ref": "disc-inv-2026-0043"
    }
    ```

    `sub_id` exactement 12 caractères, `period` au format `MM/YYYY`, `amount` jusqu'à 20 caractères, `pay_key` exactement 7 caractères.
  </Accordion>

  <Accordion title="electronic_payment_key — clé de 25 caractères">
    Acceptée pour ADE et SEAAL comme alternative à `reference`. Elle doit faire **exactement 25 caractères**.

    ```json theme={null}
    {
      "partner": "ADE",
      "account": { "electronic_payment_key": "0123456789012345678901234" },
      "ref": "disc-inv-2026-0044"
    }
    ```
  </Accordion>
</AccordionGroup>

<Warning>
  **Exactement un identifiant.** `reference`, `contractNumber`, `aadlNumber` et `electronic_payment_key` comptent pour le même emplacement, tout comme `phoneNumber` et `phone_number` ; `sonelgaz` et `ade` occupent chacun leur propre emplacement. N'en envoyer aucun, ou en envoyer deux, est rejeté avec un `400`.
</Warning>

## Réponse

<ResponseField name="success" type="boolean" required>
  `true` lorsque la découverte a été acceptée.
</ResponseField>

<ResponseField name="data" type="object" required>
  <Expandable title="properties">
    <ResponseField name="transactionId" type="string" required>
      La transaction à interroger. Une chaîne hexadécimale minuscule de 24 caractères.
    </ResponseField>

    <ResponseField name="ref" type="string" required>
      La `ref` que vous avez envoyée, renvoyée telle quelle.
    </ResponseField>

    <ResponseField name="status" type="string" required>
      Toujours `PENDING` à ce stade.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="meta" type="object" required>
  <Expandable title="properties">
    <ResponseField name="timestamp" type="string" required>
      Heure de la réponse, ISO 8601 UTC.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="requestId" type="string" required>
  Identifiant de corrélation, également envoyé dans l'en-tête de réponse `X-Request-Id`.
</ResponseField>

## Exemples

<CodeGroup>
  ```bash cURL theme={null}
  curl https://billapi.oneclickdz.com/v3/bills/discover \
    -X POST \
    -H "Content-Type: application/json" \
    -H "X-Access-Token: YOUR_API_KEY" \
    -d '{
      "partner": "ADE",
      "account": { "reference": "0123456789012345678901234" },
      "ref": "disc-inv-2026-0042"
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch(
    "https://billapi.oneclickdz.com/v3/bills/discover",
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Access-Token": process.env.ONECLICKDZ_API_KEY,
      },
      body: JSON.stringify({
        partner: "ADE",
        account: { reference: "0123456789012345678901234" },
        ref: "disc-inv-2026-0042",
      }),
    },
  );

  const body = await response.json();

  if (!body.success) {
    throw new Error(`${body.error.code}: ${body.error.message}`);
  }

  // Accepted — the bills are not here yet. Poll this id until READY.
  console.log(body.data.transactionId);
  ```

  ```python Python theme={null}
  import os
  import requests

  response = requests.post(
      'https://billapi.oneclickdz.com/v3/bills/discover',
      headers={
          'Content-Type': 'application/json',
          'X-Access-Token': os.getenv('ONECLICKDZ_API_KEY')
      },
      json={
          'partner': 'ADE',
          'account': {'reference': '0123456789012345678901234'},
          'ref': 'disc-inv-2026-0042'
      }
  )

  body = response.json()

  if not body['success']:
      raise RuntimeError(f"{body['error']['code']}: {body['error']['message']}")

  # Accepted — the bills are not here yet. Poll this id until READY.
  print(body['data']['transactionId'])
  ```

  ```php PHP theme={null}
  <?php
  $payload = [
      'partner' => 'ADE',
      'account' => ['reference' => '0123456789012345678901234'],
      'ref'     => 'disc-inv-2026-0042'
  ];

  $ch = curl_init('https://billapi.oneclickdz.com/v3/bills/discover');
  curl_setopt($ch, CURLOPT_POST, true);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'Content-Type: application/json',
      'X-Access-Token: ' . getenv('ONECLICKDZ_API_KEY')
  ]);
  curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));

  $body = json_decode(curl_exec($ch), true);
  curl_close($ch);

  if (!$body['success']) {
      throw new Exception($body['error']['code'] . ': ' . $body['error']['message']);
  }

  // Accepted — the bills are not here yet. Poll this id until READY.
  echo $body['data']['transactionId'];
  ?>
  ```
</CodeGroup>

### Réponse de Succès

```json theme={null}
{
  "success": true,
  "data": {
    "transactionId": "68b2f4c1a7d3e9f204c81a55",
    "ref": "disc-inv-2026-0042",
    "status": "PENDING"
  },
  "meta": {
    "timestamp": "2026-08-31T10:15:32.194Z"
  },
  "requestId": "req_9f3a1c72e0b84d51aB3xZq07"
}
```

Interrogez [Obtenir une Transaction par ID](/fr/api-reference/bill-payment/check-by-id) jusqu'à ce que `status` soit `READY`, puis lisez `bills[]` :

```json theme={null}
{
  "success": true,
  "data": {
    "transactionId": "68b2f4c1a7d3e9f204c81a55",
    "ref": "disc-inv-2026-0042",
    "type": "discovery",
    "status": "READY",
    "partner": "ADE",
    "account": { "reference": "0123456789012345678901234" },
    "bills": [
      {
        "billId": "sbx_bill_68b2f4c1a7d3e9f204c81a55_0",
        "amount": 443.39,
        "fee": 30.00,
        "label": "Facture ADE"
      }
    ],
    "currency": "DZD",
    "createdAt": "2026-08-31T10:15:32.194Z",
    "updatedAt": "2026-08-31T10:15:33.008Z",
    "completedAt": "2026-08-31T10:15:33.008Z"
  },
  "meta": {
    "timestamp": "2026-08-31T10:15:34.120Z"
  },
  "requestId": "req_9f3a1c72e0b84d51aB3xZq07"
}
```

## Réponses d'Erreur

<AccordionGroup>
  <Accordion title="400 — Erreur de validation">
    **Le corps de la requête ne correspondait pas au schéma.**

    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "ERR_VALIDATION",
        "message": "ref is required",
        "details": ["ref is required"]
      },
      "requestId": "req_9f3a1c72e0b84d51aB3xZq07"
    }
    ```

    `details` liste tous les champs en échec, pas seulement le premier. Causes courantes : une `ref` manquante, un `partner` qui ne fait pas partie des cinq valeurs, un `account` sans identifiant ou avec deux, une `electronic_payment_key` qui ne fait pas exactement 25 caractères, un `phoneNumber` qui n'est pas une ligne fixe algérienne valide.

    **Que faire :** corrigez la requête. La réessayer telle quelle retourne la même erreur.
  </Accordion>

  <Accordion title="400 — Compte invalide">
    **L'identifiant est structurellement acceptable mais inutilisable pour ce partenaire.**

    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "INVALID_ACCOUNT",
        "message": "The provided account identifier is invalid."
      },
      "requestId": "req_9f3a1c72e0b84d51aB3xZq07"
    }
    ```

    **Que faire :** demandez au client de vérifier le numéro inscrit sur sa facture. C'est l'erreur à lui présenter ; `ERR_VALIDATION` est destinée à vos journaux.
  </Accordion>

  <Accordion title="401 — Jeton d'accès manquant ou invalide">
    **La clé était absente ou a été rejetée.**

    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "INVALID_ACCESS_TOKEN",
        "message": "The provided access token is invalid."
      },
      "requestId": "req_9f3a1c72e0b84d51aB3xZq07"
    }
    ```

    Un en-tête manquant retourne `MISSING_ACCESS_TOKEN` à la place, avec le même statut HTTP.

    **Que faire :** vérifiez la clé avec [Valider la Clé API](/fr/api-reference/bill-payment/validate-key).
  </Accordion>

  <Accordion title="403 — Référence en double">
    **Vous avez déjà utilisé cette `ref` pour ce partenaire.**

    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "DUPLICATED_REF",
        "message": "A transaction with this ref already exists for this partner."
      },
      "requestId": "req_9f3a1c72e0b84d51aB3xZq07"
    }
    ```

    **Que faire :** ne réessayez **pas** aveuglément avec une nouvelle `ref` — vous démarreriez une deuxième découverte pour le même compte. Retrouvez la découverte existante avec [Obtenir une Transaction par Référence](/fr/api-reference/bill-payment/check-by-ref) et poursuivez à partir de son état.
  </Accordion>

  <Accordion title="409 — Facture déjà payée">
    **Ce compte a déjà été payé récemment, une nouvelle découverte est donc refusée.**

    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "BILL_ALREADY_PAID",
        "message": "This bill has already been paid."
      },
      "requestId": "req_9f3a1c72e0b84d51aB3xZq07"
    }
    ```

    **Que faire :** c'est une protection contre le double paiement, pas un échec. Retrouvez la transaction réussie dans [Lister les Transactions](/fr/api-reference/bill-payment/list-transactions) et montrez ce reçu au client.
  </Accordion>

  <Accordion title="409 — Paiement en cours">
    **Un autre paiement pour ce compte n'est pas encore terminé.**

    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "PAYMENT_IN_PROGRESS",
        "message": "A payment for this account is currently in progress."
      },
      "requestId": "req_9f3a1c72e0b84d51aB3xZq07"
    }
    ```

    **Que faire :** attendez que ce paiement atteigne un état final, puis recommencez. Ne lancez pas les deux en parallèle.
  </Accordion>

  <Accordion title="503 — Partenaire indisponible">
    **Le facturier est injoignable, ou est actuellement désactivé.**

    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "PARTNER_UNAVAILABLE",
        "message": "Partner temporarily unavailable — please try again later."
      },
      "requestId": "req_9f3a1c72e0b84d51aB3xZq07"
    }
    ```

    **Que faire :** rafraîchissez [Lister les Partenaires](/fr/api-reference/bill-payment/list-partners) et réessayez plus tard. Aucune transaction n'a été créée et rien n'a été débité. Cette réponse ne comporte pas de `Retry-After` ; espacez vos tentatives de votre côté.
  </Accordion>

  <Accordion title="503 — Authentification indisponible">
    **Nous n'avons pas pu vérifier votre clé à temps. Votre clé n'est pas en cause.**

    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "AUTH_UNAVAILABLE",
        "message": "Authentication is temporarily unavailable. Please retry shortly."
      },
      "requestId": "req_9f3a1c72e0b84d51aB3xZq07"
    }
    ```

    **Que faire :** attendez le `Retry-After` (5 secondes) et réessayez la même requête avec la même `ref`.
  </Accordion>

  <Accordion title="503 — Service indisponible">
    **L'API Bill Payment est en maintenance planifiée.**

    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "SERVICE_UNAVAILABLE",
        "message": "The service is temporarily unavailable. Please retry shortly."
      },
      "requestId": "req_9f3a1c72e0b84d51aB3xZq07"
    }
    ```

    **Que faire :** respectez `Retry-After` et réessayez avec la même `ref`.
  </Accordion>

  <Accordion title="500 — Erreur interne">
    **Quelque chose a échoué de notre côté.**

    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "INTERNAL_ERROR",
        "message": "An unexpected error occurred."
      },
      "requestId": "req_9f3a1c72e0b84d51aB3xZq07"
    }
    ```

    **Que faire :** vérifiez si la découverte a bien été créée avec [Obtenir une Transaction par Référence](/fr/api-reference/bill-payment/check-by-ref) avant de réessayer, et envoyez le `requestId` au support si le problème persiste.
  </Accordion>
</AccordionGroup>

## Le seuil de découverte de 200 DZD

Les factures inférieures à **200 DZD** sont filtrées pendant la découverte et n'apparaissent jamais dans `bills[]`.

Une transaction `READY` avec un `bills[]` vide signifie donc l'une de deux choses, et l'API ne fait pas la distinction entre elles :

* le compte ne doit rien, ou
* tout ce qu'il doit est en dessous du seuil de 200 DZD.

<Note>
  Formulez cela avec soin pour vos clients. « Aucune facture n'est payable pour le moment » est exact ; « vous ne devez rien » ne l'est pas.
</Note>

## Prévention des requêtes dupliquées

`ref` rend une découverte sûre à réessayer. Si une erreur réseau masque la réponse, recherchez la `ref` au lieu d'envoyer une deuxième découverte.

```javascript theme={null}
async function discoverSafely(partner, account, ref) {
  try {
    const response = await fetch(
      "https://billapi.oneclickdz.com/v3/bills/discover",
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-Access-Token": process.env.ONECLICKDZ_API_KEY,
        },
        body: JSON.stringify({ partner, account, ref }),
      },
    );

    const body = await response.json();

    if (body.success) {
      return body.data.transactionId;
    }

    // Already started earlier — recover it instead of starting a second one.
    if (body.error.code === "DUPLICATED_REF") {
      return await findByRef(ref, partner);
    }

    throw new Error(`${body.error.code}: ${body.error.message}`);
  } catch (networkError) {
    // The request may still have been accepted. Check before retrying.
    const existing = await findByRef(ref, partner).catch(() => null);
    if (existing) return existing;
    throw networkError;
  }
}

async function findByRef(ref, partner) {
  const url = new URL(
    "https://billapi.oneclickdz.com/v3/bills/transactions/by-ref",
  );
  url.searchParams.set("ref", ref);
  url.searchParams.set("partner", partner);

  const response = await fetch(url, {
    headers: { "X-Access-Token": process.env.ONECLICKDZ_API_KEY },
  });

  const body = await response.json();
  if (!body.success) throw new Error(body.error.code);
  return body.data.transactionId;
}
```

Une bonne `ref` est dérivée de quelque chose que vous stockez déjà — votre propre identifiant de facture ou de commande — afin de pouvoir toujours la reconstruire. Voir [Découvrir les factures](/fr/bill-payment-guides/2-discovering-bills) pour une recette de nommage.

## Cycle de Vie du Statut

<Steps>
  <Step title="PENDING">
    La réponse que vous venez de recevoir. La découverte est en file d'attente et en cours d'exécution.
  </Step>

  <Step title="READY">
    Découverte terminée. `bills[]` est présent — possiblement vide. Choisissez un `billId` et appelez [Payer une Facture](/fr/api-reference/bill-payment/pay-bill).
  </Step>

  <Step title="FAILED">
    La découverte n'a pas pu aboutir. `error.code` explique pourquoi : `INVALID_ACCOUNT`, `PARTNER_UNAVAILABLE`, `BILL_ALREADY_PAID` ou `PAYMENT_DECLINED`.
  </Step>
</Steps>

[Référence complète des statuts →](/fr/api-reference/bill-payment/check-by-id#status-lifecycle)

## Bonnes Pratiques

<CardGroup cols={2}>
  <Card title="Vérifiez d'abord le partenaire" icon="building-columns">
    Une carte des partenaires mise en cache vous permet de masquer un facturier indisponible avant que le client ne saisisse un numéro de compte.
  </Card>

  <Card title="Dérivez la ref, ne l'inventez pas" icon="fingerprint">
    Construisez `ref` à partir de votre propre identifiant de commande afin de pouvoir toujours retrouver la transaction.
  </Card>

  <Card title="Ne supposez jamais que 200 signifie vide" icon="triangle-exclamation">
    Les factures arrivent dans la transaction, pas dans cette réponse. Interrogez avant d'annoncer quoi que ce soit à un client.
  </Card>

  <Card title="Lisez fee depuis la réponse" icon="calculator">
    Chaque facture porte son propre `fee`. Ne le recalculez pas dans votre propre code.
  </Card>
</CardGroup>

## Endpoints Associés

<CardGroup cols={3}>
  <Card title="Lister les Partenaires" icon="building-columns" href="/fr/api-reference/bill-payment/list-partners">
    Vérifiez d'abord la disponibilité
  </Card>

  <Card title="Obtenir une Transaction par ID" icon="id-card" href="/fr/api-reference/bill-payment/check-by-id">
    Interroger pour obtenir les factures
  </Card>

  <Card title="Payer une Facture" icon="money-bill-transfer" href="/fr/api-reference/bill-payment/pay-bill">
    En payer une
  </Card>

  <Card title="Obtenir une Transaction par Référence" icon="tag" href="/fr/api-reference/bill-payment/check-by-ref">
    Récupérer une réponse perdue
  </Card>

  <Card title="Découvrir les Factures" icon="magnifying-glass" href="/fr/bill-payment-guides/2-discovering-bills">
    Le guide complet
  </Card>

  <Card title="Partenaires et Comptes" icon="address-card" href="/fr/bill-payment-guides/1-partners-and-accounts">
    Règles d'identifiant par partenaire
  </Card>
</CardGroup>
