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

# Exportar consentimentos

> CSV em streaming e jobs assíncronos para exports muito grandes

A API de exportação dá ao seu backend duas formas de extrair os registros de consentimento de um site em CSV: **streaming síncrono** para volumes até \~100k linhas, ou **jobs assíncronos** para volumes maiores. Ambas as rotas aceitam `Authorization: Bearer sk-...` e consomem **1** unidade da cota mensal por chamada (o feature `consent_export` está disponível a partir do plano Starter).

> A funcionalidade `consent_export` é gerenciada server-side: contas Free recebem `403 plan_limit_exceeded` ao chamar qualquer rota desta página.

## Quando usar cada modo

| Cenário                               | Endpoint                                                | Resposta                                    |
| ------------------------------------- | ------------------------------------------------------- | ------------------------------------------- |
| Site com **\< 100k** linhas           | `GET /api/v1/sites/{siteId}/consents/export?format=csv` | `200` com `text/csv` em streaming           |
| Site com **> 100k** linhas (auto)     | mesma rota acima                                        | `202` com `id` (vira async automaticamente) |
| Forçar async (qualquer volume)        | mesma rota + `?async=true`                              | `202` com `id`                              |
| Sempre async, independente do tamanho | `POST /api/v1/sites/{siteId}/exports`                   | `202` com `id`                              |

A trilha de auditoria (cap de 5 exports por período no plano Starter / ilimitado no Pro+) é compartilhada — não dá para burlar trocando de rota.

## Modo síncrono (streaming CSV)

`GET /api/v1/sites/{siteId}/consents/export?format=csv` responde com `Content-Type: text/csv; charset=utf-8`, `Content-Disposition: attachment; filename="consents-<domain>-<YYYYMMDD>.csv"` e BOM UTF-8 no início (Excel abre sem perguntar encoding). A paginação por *keyset* mantém o uso de memória `O(batch_size)` mesmo em sites grandes — você pode salvar direto num arquivo.

Colunas: `consent_id, accepted, analytics, marketing, country, region, user_agent, policy_version, created_at`.

<CodeGroup>
  ```bash cURL theme={null}
  SITE_ID=6b8b4567-327a-4d10-92a5-9e8c4f5a7d12
  curl -L \
    -H "Authorization: Bearer $CONSENTFLY_API_KEY" \
    -o consents.csv \
    "https://www.consentfly.com.br/api/v1/sites/$SITE_ID/consents/export?format=csv"
  ```

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

  const siteId = "6b8b4567-327a-4d10-92a5-9e8c4f5a7d12";
  const res = await fetch(
    `https://www.consentfly.com.br/api/v1/sites/${siteId}/consents/export?format=csv`,
    { headers: { Authorization: `Bearer ${process.env.CONSENTFLY_API_KEY}` } }
  );
  await writeFile("consents.csv", Buffer.from(await res.arrayBuffer()));
  ```

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

  site_id = "6b8b4567-327a-4d10-92a5-9e8c4f5a7d12"
  with requests.get(
      f"https://www.consentfly.com.br/api/v1/sites/{site_id}/consents/export",
      headers={"Authorization": f"Bearer {os.environ['CONSENTFLY_API_KEY']}"},
      params={"format": "csv"},
      stream=True,
  ) as res:
      with open("consents.csv", "wb") as f:
          for chunk in res.iter_content(chunk_size=8192):
              f.write(chunk)
  ```

  ```go Go theme={null}
  siteID := "6b8b4567-327a-4d10-92a5-9e8c4f5a7d12"
  url := "https://www.consentfly.com.br/api/v1/sites/" + siteID + "/consents/export?format=csv"
  req, _ := http.NewRequest("GET", url, nil)
  req.Header.Set("Authorization", "Bearer "+os.Getenv("CONSENTFLY_API_KEY"))
  res, _ := http.DefaultClient.Do(req)
  defer res.Body.Close()
  f, _ := os.Create("consents.csv")
  defer f.Close()
  io.Copy(f, res.Body)
  ```
</CodeGroup>

## Modo assíncrono (enqueue + status + download)

Quando o site cruza \~100k linhas (ou quando você força com `?async=true`), a mesma rota responde `202 Accepted`. Para sempre enfileirar (independente do tamanho), use `POST /api/v1/sites/{siteId}/exports`.

### Enfileirar um export

<CodeGroup>
  ```bash cURL theme={null}
  SITE_ID=6b8b4567-327a-4d10-92a5-9e8c4f5a7d12
  curl -X POST \
    -H "Authorization: Bearer $CONSENTFLY_API_KEY" \
    "https://www.consentfly.com.br/api/v1/sites/$SITE_ID/exports"
  ```

  ```javascript Node theme={null}
  const siteId = "6b8b4567-327a-4d10-92a5-9e8c4f5a7d12";
  const res = await fetch(
    `https://www.consentfly.com.br/api/v1/sites/${siteId}/exports`,
    { method: "POST", headers: { Authorization: `Bearer ${process.env.CONSENTFLY_API_KEY}` } }
  );
  const { id, status_url } = await res.json();
  ```

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

  site_id = "6b8b4567-327a-4d10-92a5-9e8c4f5a7d12"
  res = requests.post(
      f"https://www.consentfly.com.br/api/v1/sites/{site_id}/exports",
      headers={"Authorization": f"Bearer {os.environ['CONSENTFLY_API_KEY']}"},
  )
  job = res.json()  # { id, status: "queued", status_url, message }
  ```

  ```go Go theme={null}
  siteID := "6b8b4567-327a-4d10-92a5-9e8c4f5a7d12"
  url := "https://www.consentfly.com.br/api/v1/sites/" + siteID + "/exports"
  req, _ := http.NewRequest("POST", url, nil)
  req.Header.Set("Authorization", "Bearer "+os.Getenv("CONSENTFLY_API_KEY"))
  http.DefaultClient.Do(req)
  ```
</CodeGroup>

A resposta `202` traz `id` (use-o para polling), `status: "queued"`, e `status_url` para consulta.

### Consultar status do job

`GET /api/v1/exports/{id}` devolve o `ExportDTO` com o status atual. Quando `status="ready"`, o campo `download_url` é uma URL assinada com TTL de 10 minutos.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://www.consentfly.com.br/api/v1/exports/exp_01HK8WD2QXKB4R7N9F1H3P5T0G \
    -H "Authorization: Bearer $CONSENTFLY_API_KEY"
  ```

  ```javascript Node theme={null}
  const exportId = "exp_01HK8WD2QXKB4R7N9F1H3P5T0G";
  const res = await fetch(`https://www.consentfly.com.br/api/v1/exports/${exportId}`, {
    headers: { Authorization: `Bearer ${process.env.CONSENTFLY_API_KEY}` },
  });
  const job = await res.json();
  ```

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

  export_id = "exp_01HK8WD2QXKB4R7N9F1H3P5T0G"
  res = requests.get(
      f"https://www.consentfly.com.br/api/v1/exports/{export_id}",
      headers={"Authorization": f"Bearer {os.environ['CONSENTFLY_API_KEY']}"},
  )
  job = res.json()
  ```

  ```go Go theme={null}
  exportID := "exp_01HK8WD2QXKB4R7N9F1H3P5T0G"
  req, _ := http.NewRequest("GET", "https://www.consentfly.com.br/api/v1/exports/"+exportID, nil)
  req.Header.Set("Authorization", "Bearer "+os.Getenv("CONSENTFLY_API_KEY"))
  http.DefaultClient.Do(req)
  ```
</CodeGroup>

### Baixar o arquivo pronto

`GET /api/v1/exports/{id}/download` retorna um `302` para uma URL assinada (TTL 10 min). Use `-L` no cURL para seguir o redirect. Se o token expirar, basta chamar `GET /exports/{id}` de novo — uma URL fresca é mintada a cada chamada.

<CodeGroup>
  ```bash cURL theme={null}
  curl -L \
    -H "Authorization: Bearer $CONSENTFLY_API_KEY" \
    -o consents.csv \
    "https://www.consentfly.com.br/api/v1/exports/exp_01HK8WD2QXKB4R7N9F1H3P5T0G/download"
  ```

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

  const exportId = "exp_01HK8WD2QXKB4R7N9F1H3P5T0G";
  const res = await fetch(
    `https://www.consentfly.com.br/api/v1/exports/${exportId}/download`,
    { headers: { Authorization: `Bearer ${process.env.CONSENTFLY_API_KEY}` }, redirect: "follow" }
  );
  await writeFile("consents.csv", Buffer.from(await res.arrayBuffer()));
  ```

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

  export_id = "exp_01HK8WD2QXKB4R7N9F1H3P5T0G"
  with requests.get(
      f"https://www.consentfly.com.br/api/v1/exports/{export_id}/download",
      headers={"Authorization": f"Bearer {os.environ['CONSENTFLY_API_KEY']}"},
      allow_redirects=True,
      stream=True,
  ) as res:
      with open("consents.csv", "wb") as f:
          for chunk in res.iter_content(chunk_size=8192):
              f.write(chunk)
  ```

  ```go Go theme={null}
  exportID := "exp_01HK8WD2QXKB4R7N9F1H3P5T0G"
  url := "https://www.consentfly.com.br/api/v1/exports/" + exportID + "/download"
  req, _ := http.NewRequest("GET", url, nil)
  req.Header.Set("Authorization", "Bearer "+os.Getenv("CONSENTFLY_API_KEY"))
  res, _ := http.DefaultClient.Do(req)
  defer res.Body.Close()
  f, _ := os.Create("consents.csv")
  defer f.Close()
  io.Copy(f, res.Body)
  ```
</CodeGroup>

### Estados possíveis

| Status     | Significado                                                   |
| ---------- | ------------------------------------------------------------- |
| `queued`   | Esperando o worker pegar                                      |
| `running`  | Worker está escrevendo o arquivo                              |
| `ready`    | Arquivo pronto, `download_url` disponível                     |
| `failed`   | Falha durante a escrita — `error_message` traz o detalhe      |
| `expired`  | Passados 7 dias, o cron de limpeza removeu o arquivo do disco |
| `streamed` | Foi servido inline (modo síncrono) — sem arquivo persistente  |

### Listar seus exports recentes

`GET /api/v1/exports?limit=20` retorna os exports mais recentes (máximo 100 por chamada).

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://www.consentfly.com.br/api/v1/exports?limit=20" \
    -H "Authorization: Bearer $CONSENTFLY_API_KEY"
  ```

  ```javascript Node theme={null}
  const res = await fetch("https://www.consentfly.com.br/api/v1/exports?limit=20", {
    headers: { Authorization: `Bearer ${process.env.CONSENTFLY_API_KEY}` },
  });
  const { data } = await res.json();
  ```

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

  res = requests.get(
      "https://www.consentfly.com.br/api/v1/exports",
      headers={"Authorization": f"Bearer {os.environ['CONSENTFLY_API_KEY']}"},
      params={"limit": 20},
  )
  exports = res.json()["data"]
  ```

  ```go Go theme={null}
  req, _ := http.NewRequest("GET", "https://www.consentfly.com.br/api/v1/exports?limit=20", nil)
  req.Header.Set("Authorization", "Bearer "+os.Getenv("CONSENTFLY_API_KEY"))
  http.DefaultClient.Do(req)
  ```
</CodeGroup>

## Limites e retenção

* **Cota mensal:** cada chamada API-key conta 1 unidade contra `request_limit`.
* **Cap por período de cobrança:** Starter 5 exports / período, Pro+ ilimitado.
* **Retenção em disco:** arquivos `ready` vivem **7 dias**; depois flipam para `expired` e o download retorna `410 Gone`.
* **Cross-tenant:** chamadas para `id`s que não pertencem à sua conta retornam `404` (nunca `403`, para não vazar existência).

## Erros frequentes

| Código HTTP | `error`               | Quando                                                       |
| ----------- | --------------------- | ------------------------------------------------------------ |
| `401`       | `unauthorized`        | API Key inválida, revogada ou token de download expirado     |
| `403`       | `plan_limit_exceeded` | Plano Free tentando usar `consent_export`                    |
| `404`       | `not_found`           | Export ou site não pertence à sua conta                      |
| `409`       | `conflict`            | Tentou baixar um export ainda em `queued`/`running`/`failed` |
| `410`       | `not_found`           | Arquivo foi expurgado (passados os 7 dias)                   |
| `429`       | `quota_exceeded`      | Cota mensal esgotada ou cap de exports do período atingido   |

## Próximo passo

Exports cobrem o **passado**: você pega os dados que já estão armazenados. Para reagir a eventos em **tempo real** (todo novo consentimento, toda mudança), configure [Webhooks](./webhooks) — o ConsentFly empurra o evento assinado para o seu endpoint assim que acontece, e seu pipeline fica sempre sincronizado sem polling.

<p align="center">
  Dúvidas? <a href="mailto:suporte@consentfly.com.br">[suporte@consentfly.com.br](mailto:suporte@consentfly.com.br)</a>
</p>
