> ## 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.

# Get Transaction by ID

> Read the full state of a bill discovery or payment

## Overview

Returns the complete transaction object — the canonical view of a Bill Payment discovery or payment. Every other Bill Payment endpoint either returns this same object or returns an identifier that points at it.

Because discovery and payment are asynchronous, this endpoint is where the real outcome appears. The `200` you received from `discover` or `pay` only confirmed that the request was accepted.

<Note>
  A transaction that belongs to another partner returns `404`, not `403`. The API never confirms that someone else's transaction exists. The same applies across environments: a sandbox key cannot read a production transaction, and vice versa.
</Note>

## Path Parameters

<ParamField path="transactionId" type="string" required>
  The transaction identifier returned by [Discover Bills](/en/api-reference/bill-payment/discover-bills) or [Pay a Bill](/en/api-reference/bill-payment/pay-bill).

  A 24-character lowercase hexadecimal string, for example `68b2f4c1a7d3e9f204c81a55`.
</ParamField>

## Response

<ResponseField name="success" type="boolean" required>
  `true` when the transaction was found and returned.
</ResponseField>

<ResponseField name="data" type="object" required>
  The transaction object.

  <Expandable title="properties">
    <ResponseField name="transactionId" type="string" required>
      The transaction identifier. Always present.
    </ResponseField>

    <ResponseField name="ref" type="string" required>
      The idempotency reference you supplied when you started the discovery. This is the value [Get Transaction by Reference](/en/api-reference/bill-payment/check-by-ref) looks up.
    </ResponseField>

    <ResponseField name="type" type="string" required>
      `discovery` while the transaction has only looked bills up, `payment` once you have submitted a payment against it.
    </ResponseField>

    <ResponseField name="status" type="string" required>
      One of `PENDING`, `READY`, `PROCESSING`, `SUCCESS`, `FAILED`, `REFUNDED`, `UNKNOWN`. See [Status lifecycle](#status-lifecycle).
    </ResponseField>

    <ResponseField name="partner" type="string" required>
      One of `ADE`, `SONELGAZ`, `SEAAL`, `AADL`, `Algérie Télécom`.
    </ResponseField>

    <ResponseField name="account" type="object" required>
      The account the transaction is for, keyed by that partner's identifier field: `reference` for ADE and SEAAL, `contractNumber` for SONELGAZ, `aadlNumber` for AADL, `phoneNumber` for Algérie Télécom.
    </ResponseField>

    <ResponseField name="currency" type="string" required>
      Always `DZD`.
    </ResponseField>

    <ResponseField name="createdAt" type="string" required>
      When the transaction was created, ISO 8601 UTC.
    </ResponseField>

    <ResponseField name="updatedAt" type="string" required>
      When the transaction last changed, ISO 8601 UTC.
    </ResponseField>

    <ResponseField name="completedAt" type="string | null" required>
      When the transaction stopped working. `null` while it is still in progress. Branch on `status`, never on this field.
    </ResponseField>

    <ResponseField name="bills" type="array">
      Present **only when `status` is `READY`**. The bills that are due and payable. An empty array means there is nothing to pay.

      <Expandable title="bill properties">
        <ResponseField name="billId" type="string" required>
          The identifier to send to [Pay a Bill](/en/api-reference/bill-payment/pay-bill).
        </ResponseField>

        <ResponseField name="amount" type="number" required>
          What the partner is owed, in DZD, 2 decimals.
        </ResponseField>

        <ResponseField name="fee" type="number" required>
          The OneClickDz service fee for paying this bill, in DZD, 2 decimals. Read it from the response — never recompute it.
        </ResponseField>

        <ResponseField name="label" type="string">
          A human-readable description, when the partner supplied one.
        </ResponseField>

        <ResponseField name="period" type="string">
          The billing period, when the partner supplied one.
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="selectedBill" type="object">
      Present once a bill has been selected for payment. Same shape as a `bills[]` entry, and it is the bill that was actually charged.
    </ResponseField>

    <ResponseField name="total" type="number">
      Present with `selectedBill`. `amount + fee` — the figure debited from your balance.
    </ResponseField>

    <ResponseField name="receiptUrl" type="string">
      Present **only when `status` is `SUCCESS`**. The absolute URL of [the receipt download](/en/api-reference/bill-payment/get-receipt).
    </ResponseField>

    <ResponseField name="operationId" type="string">
      Present **only when `status` is `SUCCESS`**. The partner's proof-of-payment reference. Store it — it is what a customer dispute is settled with.
    </ResponseField>

    <ResponseField name="error" type="object">
      Present **only when `status` is `FAILED` or `REFUNDED`**.

      <Expandable title="properties">
        <ResponseField name="code" type="string" required>
          One of `PAYMENT_DECLINED`, `PARTNER_UNAVAILABLE`, `INVALID_ACCOUNT`, `BILL_ALREADY_PAID`.
        </ResponseField>

        <ResponseField name="message" type="string" required>
          A short explanation, safe to log. Do not parse it — branch on `code`.
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="meta" type="object" required>
  <Expandable title="properties">
    <ResponseField name="timestamp" type="string" required>
      Response time, ISO 8601 UTC.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="requestId" type="string" required>
  Correlation identifier, also sent as the `X-Request-Id` response header. Log it — it is the only thing support needs to trace a request.
</ResponseField>

## Examples

<CodeGroup>
  ```bash cURL theme={null}
  curl https://billapi.oneclickdz.com/v3/bills/transactions/68b2f4c1a7d3e9f204c81a55 \
    -H "X-Access-Token: YOUR_API_KEY"
  ```

  ```javascript Node.js theme={null}
  const transactionId = "68b2f4c1a7d3e9f204c81a55";

  const response = await fetch(
    `https://billapi.oneclickdz.com/v3/bills/transactions/${transactionId}`,
    { headers: { "X-Access-Token": process.env.ONECLICKDZ_API_KEY } },
  );

  const body = await response.json();

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

  console.log(body.data.status);
  ```

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

  transaction_id = '68b2f4c1a7d3e9f204c81a55'

  response = requests.get(
      f'https://billapi.oneclickdz.com/v3/bills/transactions/{transaction_id}',
      headers={'X-Access-Token': os.getenv('ONECLICKDZ_API_KEY')}
  )

  body = response.json()

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

  print(body['data']['status'])
  ```

  ```php PHP theme={null}
  <?php
  $transactionId = '68b2f4c1a7d3e9f204c81a55';

  $ch = curl_init("https://billapi.oneclickdz.com/v3/bills/transactions/$transactionId");
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'X-Access-Token: ' . getenv('ONECLICKDZ_API_KEY')
  ]);

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

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

  echo $body['data']['status'];
  ?>
  ```
</CodeGroup>

### Success Response

A completed payment:

```json theme={null}
{
  "success": true,
  "data": {
    "transactionId": "68b2f4c1a7d3e9f204c81a55",
    "ref": "disc-inv-2026-0042",
    "type": "payment",
    "status": "SUCCESS",
    "partner": "ADE",
    "account": {
      "reference": "0123456789012345678901234"
    },
    "selectedBill": {
      "billId": "sbx_bill_68b2f4c1a7d3e9f204c81a55_0",
      "amount": 443.39,
      "fee": 30.00,
      "label": "Facture ADE"
    },
    "total": 473.39,
    "currency": "DZD",
    "receiptUrl": "https://billapi.oneclickdz.com/v3/bills/transactions/68b2f4c1a7d3e9f204c81a55/receipt",
    "operationId": "op_7726351904",
    "createdAt": "2026-08-31T10:15:32.194Z",
    "updatedAt": "2026-08-31T10:15:41.902Z",
    "completedAt": "2026-08-31T10:15:41.902Z"
  },
  "meta": {
    "timestamp": "2026-08-31T10:16:03.771Z"
  },
  "requestId": "req_9f3a1c72e0b84d51aB3xZq07"
}
```

A discovery that finished and found one payable bill:

```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"
}
```

A payment that was declined and refunded in full:

```json theme={null}
{
  "success": true,
  "data": {
    "transactionId": "68b2f4c1a7d3e9f204c81a55",
    "ref": "disc-inv-2026-0042",
    "type": "payment",
    "status": "REFUNDED",
    "partner": "ADE",
    "account": {
      "reference": "0123456789012340000000005"
    },
    "selectedBill": {
      "billId": "sbx_bill_68b2f4c1a7d3e9f204c81a55_0",
      "amount": 550.00,
      "fee": 30.00,
      "label": "Facture ADE"
    },
    "total": 580.00,
    "currency": "DZD",
    "error": {
      "code": "PAYMENT_DECLINED",
      "message": "The payment was declined by the bank or partner portal."
    },
    "createdAt": "2026-08-31T10:15:32.194Z",
    "updatedAt": "2026-08-31T10:15:44.310Z",
    "completedAt": "2026-08-31T10:15:44.310Z"
  },
  "meta": {
    "timestamp": "2026-08-31T10:15:46.002Z"
  },
  "requestId": "req_9f3a1c72e0b84d51aB3xZq07"
}
```

## Error Responses

<AccordionGroup>
  <Accordion title="401 — Missing access token">
    **The `X-Access-Token` header was not sent.**

    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "MISSING_ACCESS_TOKEN",
        "message": "X-Access-Token header is required."
      },
      "requestId": "req_9f3a1c72e0b84d51aB3xZq07"
    }
    ```

    **What to do:** send your API key in the `X-Access-Token` header on every request.
  </Accordion>

  <Accordion title="401 — Invalid access token">
    **The key was rejected.**

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

    **What to do:** check the key. Do not retry with the same value — this will not resolve on its own.
  </Accordion>

  <Accordion title="404 — Transaction not found">
    **No transaction with that identifier belongs to your account in this environment.**

    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "NOT_FOUND",
        "message": "Transaction not found."
      },
      "requestId": "req_9f3a1c72e0b84d51aB3xZq07"
    }
    ```

    **What to do:** confirm the identifier, and confirm you are using the key for the environment the transaction was created in. A malformed identifier also answers `404`.
  </Accordion>

  <Accordion title="503 — Authentication unavailable">
    **We could not verify your key in time. This is not a verdict on your key.**

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

    **What to do:** wait for the number of seconds in the `Retry-After` response header, then retry the same request. Never present this to your customer as a credential problem.
  </Accordion>

  <Accordion title="503 — Service unavailable">
    **The Bill Payment API is in planned maintenance. Every `/v3` route answers this.**

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

    **What to do:** honour `Retry-After` and retry. Reads are safe to repeat.
  </Accordion>

  <Accordion title="500 — Internal error">
    **Something failed on our side.**

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

    **What to do:** retry the read. If it persists, contact support with the `requestId`.
  </Accordion>
</AccordionGroup>

## Status lifecycle

A transaction is created by `discover`, becomes payable, and then follows the payment through to a final state.

| Status       | Meaning                                                                  | Final   |
| ------------ | ------------------------------------------------------------------------ | ------- |
| `PENDING`    | Accepted; discovery has not finished                                     | No      |
| `READY`      | Discovery finished — read `bills[]`. An empty array means nothing is due | No      |
| `PROCESSING` | Payment in flight                                                        | No      |
| `SUCCESS`    | Paid. `operationId` and `receiptUrl` are present                         | Yes     |
| `FAILED`     | Did not go through. No money moved                                       | Yes     |
| `REFUNDED`   | Money moved and was returned in full                                     | Yes     |
| `UNKNOWN`    | Outcome not yet confirmed; under review                                  | Not yet |

<Warning>
  `UNKNOWN` is not a failure. Never refund your own customer and never retry the payment while a transaction is `UNKNOWN` — keep polling. It resolves to `SUCCESS` or `REFUNDED`.
</Warning>

[Full polling strategy →](/en/bill-payment-guides/4-status-polling)

## Fields by status

Only the fields marked below are present. Do not assume a field exists because you saw it in another status.

| Field                                                                                                | `PENDING` | `READY` | `PROCESSING` | `SUCCESS` | `FAILED` | `REFUNDED` | `UNKNOWN` |
| ---------------------------------------------------------------------------------------------------- | --------- | ------- | ------------ | --------- | -------- | ---------- | --------- |
| `transactionId`, `ref`, `type`, `status`, `partner`, `account`, `currency`, `createdAt`, `updatedAt` | Yes       | Yes     | Yes          | Yes       | Yes      | Yes        | Yes       |
| `completedAt`                                                                                        | `null`    | Set     | `null`       | Set       | Set      | Set        | Set       |
| `bills`                                                                                              | —         | Yes     | —            | —         | —        | —          | —         |
| `selectedBill`, `total`                                                                              | —         | —       | Yes          | Yes       | Yes      | Yes        | Yes       |
| `receiptUrl`, `operationId`                                                                          | —         | —       | —            | Yes       | —        | —          | —         |
| `error`                                                                                              | —         | —       | —            | —         | Yes      | Yes        | —         |

`selectedBill` and `total` appear from the moment a bill is selected for payment, so they are absent on a discovery that was never paid.

## Best Practices

<CardGroup cols={2}>
  <Card title="Branch on status" icon="code-branch">
    Treat `status` as the only source of truth for the outcome. Never infer it from `completedAt` or from the HTTP status of the original request.
  </Card>

  <Card title="Store the operationId" icon="receipt">
    On `SUCCESS`, persist `operationId` and download the receipt. Together they are the proof a customer dispute needs.
  </Card>

  <Card title="Log every requestId" icon="fingerprint">
    Keep `requestId` next to your own order identifier. It is the fastest route to an answer from support.
  </Card>

  <Card title="Poll, do not retry" icon="arrows-rotate">
    A slow transaction is not a lost one. Poll this endpoint instead of resubmitting the discovery or the payment.
  </Card>
</CardGroup>

## Related Endpoints

<CardGroup cols={3}>
  <Card title="Discover Bills" icon="magnifying-glass-dollar" href="/en/api-reference/bill-payment/discover-bills">
    Start a discovery
  </Card>

  <Card title="Pay a Bill" icon="money-bill-transfer" href="/en/api-reference/bill-payment/pay-bill">
    Pay a discovered bill
  </Card>

  <Card title="Get Transaction by Reference" icon="tag" href="/en/api-reference/bill-payment/check-by-ref">
    Look up by your own `ref`
  </Card>

  <Card title="List Transactions" icon="list" href="/en/api-reference/bill-payment/list-transactions">
    Filter and paginate history
  </Card>

  <Card title="Download Receipt" icon="file-arrow-down" href="/en/api-reference/bill-payment/get-receipt">
    Get the proof of payment
  </Card>

  <Card title="Status Polling Guide" icon="arrows-rotate" href="/en/bill-payment-guides/4-status-polling">
    A production-grade poller
  </Card>
</CardGroup>
