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

# Download AADL Avis

> Download AADL's official avis de paiement for one of your own transactions

## Overview

Streams the *avis de paiement* `AADL` publishes for a housing file — the same PDF the tenant would download from the biller — for a transaction **you** own. Like [Download Receipt](/en/api-reference/bill-payment/get-receipt), it does not return the JSON envelope on success: the bytes are the body, so you can stream them straight to your own customer.

It is not the receipt. The receipt is proof that your payment went through; the avis is AADL's own statement of what the housing file owes. Only `AADL` publishes one.

<Warning>
  **The avis is addressed by transaction, never by housing file.** There is no `codeloc` in this request and none is accepted. We load the transaction, confirm it is yours in this environment, refuse it if its `partner` is not `AADL`, and read the housing file from the bill already stored on it.

  That is deliberate. AADL's own export page needs no session and answers with a PDF for any code, whether or not it exists — proxying a caller-supplied one would turn this endpoint into a way to enumerate other people's housing files. Resolving it from your own transaction makes that impossible.
</Warning>

## Path Parameters

<ParamField path="transactionId" type="string" required>
  The transaction identifier. A 24-character lowercase hexadecimal string, from [Get Transaction by ID](/en/api-reference/bill-payment/check-by-id) or from any list, belonging to an `AADL` transaction of yours.

  Unlike the receipt, the transaction does not have to be `SUCCESS`: any of your `AADL` transactions that has resolved a bill can produce an avis.
</ParamField>

## Response

On success the body is the PDF itself. Read the headers to know what you received.

<ResponseField name="Content-Type" type="header" required>
  `application/pdf`.
</ResponseField>

<ResponseField name="Content-Disposition" type="header" required>
  `attachment; filename="avis_<transactionId>.pdf"`. The name is built from the transaction identifier alone, so it carries no tenant identity.
</ResponseField>

<ResponseField name="Content-Length" type="header" required>
  The size of the file in bytes.
</ResponseField>

<ResponseField name="Cache-Control" type="header" required>
  `private, no-store`. AADL regenerates the avis on demand and replaces it every period, so there is nothing stable to cache — and it is one customer's document, never a shared one.
</ResponseField>

## Examples

<CodeGroup>
  ```bash cURL theme={null}
  # -J -O writes the file under the name the server suggests
  curl https://api.oneclickdz.com/v3/bills/transactions/68b2f4c1a7d3e9f204c81a55/avis \
    -H "X-Access-Token: YOUR_API_KEY" \
    --fail \
    --remote-header-name --remote-name
  ```

  ```javascript Node.js theme={null}
  import { writeFile } from "node:fs/promises";

  const transactionId = "68b2f4c1a7d3e9f204c81a55";

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

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`${error.error.code}: ${error.error.message}`);
  }

  const bytes = Buffer.from(await response.arrayBuffer());
  await writeFile(`avis-${transactionId}.pdf`, bytes);

  console.log(bytes.length);
  ```

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

  transaction_id = '68b2f4c1a7d3e9f204c81a55'

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

  if response.status_code != 200:
      error = response.json()
      raise RuntimeError(f"{error['error']['code']}: {error['error']['message']}")

  with open(f'avis-{transaction_id}.pdf', 'wb') as handle:
      handle.write(response.content)

  print(len(response.content))
  ```

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

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

  $bytes  = curl_exec($ch);
  $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close($ch);

  if ($status !== 200) {
      $error = json_decode($bytes, true);
      throw new Exception($error['error']['code'] . ': ' . $error['error']['message']);
  }

  file_put_contents("avis-$transactionId.pdf", $bytes);

  echo strlen($bytes);
  ?>
  ```
</CodeGroup>

### Success Response

The body is binary. The headers look like this:

```http theme={null}
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Length: 71204
Content-Disposition: attachment; filename="avis_68b2f4c1a7d3e9f204c81a55.pdf"
Cache-Control: private, no-store
```

## Error Responses

Errors are returned as JSON, in the same envelope as every other endpoint.

<AccordionGroup>
  <Accordion title="401 — Missing or invalid access token">
    **The key was absent or rejected.**

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

    **What to do:** verify the key against the [Authentication](/en/authentication) guide.
  </Accordion>

  <Accordion title="404 — Transaction not found">
    **The identifier is unknown, or the transaction is not yours in this environment.**

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

    A sandbox key never sees a production transaction, and neither sees another key's. The answer is the same in every case, so the endpoint never confirms that somebody else's transaction exists.

    **What to do:** check the identifier, and check you are using the key the transaction was created with.
  </Accordion>

  <Accordion title="404 — Not an AADL transaction">
    **The transaction is yours, but its partner does not publish an avis.**

    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "NOT_FOUND",
        "message": "This partner does not publish a downloadable avis."
      },
      "requestId": "req_9f3a1c72e0b84d51aB3xZq07"
    }
    ```

    **What to do:** read `partner` on the transaction first and only offer the download when it is `AADL`. For every other biller, [the receipt](/en/api-reference/bill-payment/get-receipt) is the document you want.
  </Accordion>

  <Accordion title="404 — No avis available yet">
    **No housing file has been resolved on this transaction, or AADL declined to produce the document.**

    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "NOT_FOUND",
        "message": "No avis is available for this transaction yet."
      },
      "requestId": "req_9f3a1c72e0b84d51aB3xZq07"
    }
    ```

    A discovery that has not reached `READY` has no bill on it yet, so there is nothing to resolve a housing file from.

    **What to do:** wait for the transaction to carry a bill, then try again. A refusal from the biller is worth one retry, not a loop.
  </Accordion>
</AccordionGroup>

## What the avis is for

<CardGroup cols={2}>
  <Card title="Showing the customer what they owe" icon="file-invoice">
    The avis carries AADL's own breakdown of the housing file. It is the document a tenant recognises.
  </Card>

  <Card title="Not a substitute for the receipt" icon="receipt">
    Only [the receipt](/en/api-reference/bill-payment/get-receipt) proves a payment went through. Store that one against your order.
  </Card>
</CardGroup>

<Note>
  Remember that an AADL housing file has exactly **one** open avis, with any arrears folded into its total — so this PDF is the whole of what the file owes, never one period out of several. See [Partners and Accounts](/en/bill-payment-guides/1-partners-and-accounts).
</Note>

## Best Practices

<CardGroup cols={2}>
  <Card title="Check the partner first" icon="building-columns">
    Offer the download only for `AADL` transactions. Every other partner answers `404 NOT_FOUND`.
  </Card>

  <Card title="Never expose the URL" icon="shield-halved">
    This URL needs your API key. Serve the bytes from your own system, behind your own authentication.
  </Card>

  <Card title="Fetch it fresh" icon="rotate">
    `no-store` is not decoration — AADL replaces the avis every period. Download it when you need it.
  </Card>

  <Card title="Never send a codeloc" icon="lock">
    There is no identifier parameter to guess. If your code builds one, it is calling the wrong thing.
  </Card>
</CardGroup>

## Related Endpoints

<CardGroup cols={2}>
  <Card title="Download Receipt" icon="file-arrow-down" href="/en/api-reference/bill-payment/get-receipt">
    Proof of a successful payment
  </Card>

  <Card title="Get Transaction by ID" icon="id-card" href="/en/api-reference/bill-payment/check-by-id">
    Where `partner` and the bills appear
  </Card>

  <Card title="Discover Bills" icon="magnifying-glass-dollar" href="/en/api-reference/bill-payment/discover-bills">
    How an AADL housing file is looked up
  </Card>

  <Card title="Partners and Accounts" icon="address-card" href="/en/bill-payment-guides/1-partners-and-accounts">
    The AADL identifier rules
  </Card>
</CardGroup>
