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

# Inicio rápido

> Tu primera factura electrónica en el sandbox, en cinco pasos.

El sandbox envía los comprobantes al ambiente de pruebas de la DGII: no tienen validez fiscal y no cuentan para tus límites mensuales.

| Ambiente | URL base | Llave |
| - | - | - |
| Sandbox (pruebas) | `https://e-cf.acpay.do/sandbox/v1` | `acpay_test_...` |
| Producción | `https://e-cf.acpay.do/v1` | `acpay_live_...` |

> [!NOTE]
>
> Una llave de un ambiente se rechaza en el otro con `401`. Mientras integras, usa solo la llave de prueba y la URL del sandbox.

**1. Consigue tu llave de prueba**

Pide a AC-PAY una llave `acpay_test_...` para tu comercio. Guárdala como un secreto de tu servidor (por ejemplo, en una variable de entorno), nunca en el código ni en una aplicación web o móvil.

```bash theme={null}
export ACPAY_TEST_KEY="acpay_test_..."
```

Envíala en cada solicitud: `Authorization: Bearer $ACPAY_TEST_KEY`.

**2. Revisa tus secuencias**

Para emitir necesitas una secuencia de e-NCF vigente del tipo que vas a usar (`usable: true`).

```bash theme={null}
curl https://e-cf.acpay.do/sandbox/v1/sequences \
  -H "Authorization: Bearer $ACPAY_TEST_KEY"
```

Si no aparece el tipo que vas a emitir, regístralo con `POST /v1/sequences`. Ver [Secuencias de e-NCF](https://dev.acpay.do/guias/secuencias.md).

**3. Emite una factura**

Cada `POST` lleva un `Idempotency-Key` único que generas tú (un UUID). Si la red falla, repites la solicitud con la misma llave y no se emite un segundo comprobante: ver [Idempotencia](https://dev.acpay.do/guias/idempotencia.md).

```bash cURL theme={null}
curl -X POST https://e-cf.acpay.do/sandbox/v1/invoices \
  -H "Authorization: Bearer $ACPAY_TEST_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "ecfType": 32,
    "paymentType": "cash",
    "items": [
      { "description": "Almuerzo ejecutivo", "quantity": 3, "unitPrice": 590, "priceIncludesItbis": true }
    ]
  }'
```

```javascript Node.js theme={null}
import { randomUUID } from 'node:crypto';

const BASE_URL = 'https://e-cf.acpay.do/sandbox/v1';

// Genera la llave una vez por factura y guárdala con ella: si reintentas,
// reutilízala para no emitir dos veces.
const idempotencyKey = randomUUID();

const response = await fetch(`${BASE_URL}/invoices`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.ACPAY_TEST_KEY}`,
    'Idempotency-Key': idempotencyKey,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    ecfType: 32,
    paymentType: 'cash',
    items: [
      { description: 'Almuerzo ejecutivo', quantity: 3, unitPrice: 590, priceIncludesItbis: true },
    ],
  }),
});

const body = await response.json();
if (!response.ok) {
  // Siempre { error: { status, message, details? } }
  throw new Error(`${response.status}: ${body.error.message}`);
}
// body.id: guárdalo para consultar el estado.
```

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

import requests

BASE_URL = "https://e-cf.acpay.do/sandbox/v1"

# Genera la llave una vez por factura y guárdala con ella: si reintentas,
# reutilízala para no emitir dos veces.
idempotency_key = str(uuid.uuid4())

response = requests.post(
    f"{BASE_URL}/invoices",
    headers={
        "Authorization": f"Bearer {os.environ['ACPAY_TEST_KEY']}",
        "Idempotency-Key": idempotency_key,
    },
    json={
        "ecfType": 32,
        "paymentType": "cash",
        "items": [
            {"description": "Almuerzo ejecutivo", "quantity": 3, "unitPrice": 590, "priceIncludesItbis": True}
        ],
    },
    timeout=60,
)

body = response.json()
if not response.ok:
    # Siempre {"error": {"status", "message", "details"?}}
    raise RuntimeError(f"{response.status_code}: {body['error']['message']}")
# body["id"]: guárdalo para consultar el estado.
```

La respuesta (`201`) trae el `id` del documento y casi siempre `status: pending`: ya se firmó y se envió a la DGII, que todavía no responde. Guarda el `id`.

**4. Espera el resultado**

El estado se actualiza solo, en segundo plano. Tienes dos formas de enterarte:

* **Webhook (recomendado):** registra una URL HTTPS de tu servidor y recibes `ecf.accepted` o `ecf.rejected`. Ver [Webhooks](https://dev.acpay.do/guias/webhooks.md).
* **Consulta:** `GET /v1/invoices/{id}` devuelve el estado actual (y lo actualiza al momento si sigue `pending`).

```bash theme={null}
curl https://e-cf.acpay.do/sandbox/v1/invoices/<id> \
  -H "Authorization: Bearer $ACPAY_TEST_KEY"
```

`accepted` o `accepted_with_observations` es un comprobante válido. `rejected` trae el motivo en `statusMessage`. Ver [Estados de un e-CF](https://dev.acpay.do/guias/estados.md).

**5. Descarga el PDF y el XML**

```bash theme={null}
curl -OJ https://e-cf.acpay.do/sandbox/v1/invoices/<id>/pdf -H "Authorization: Bearer $ACPAY_TEST_KEY"
curl -OJ https://e-cf.acpay.do/sandbox/v1/invoices/<id>/xml -H "Authorization: Bearer $ACPAY_TEST_KEY"
```

## Pruébalo sin escribir código

En la pestaña **Referencia de la API**, cada endpoint tiene un panel para probarlo: pega tu llave de prueba, completa los campos y envía la solicitud al sandbox. Las solicitudes van directo de tu navegador al sandbox de AC-PAY, sin pasar por terceros.

> [!WARNING]
>
> En el panel de pruebas usa solo tu llave `acpay_test_...`. Nunca pegues una llave de producción en un sitio web.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.