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

# Transaction batch failures

> Download CSV or JSON reports listing failed transaction rows for a batch import job, including row numbers, error codes, and validation messages.

## Endpoints

```
GET https://api.gu1.ai/batch-import/transaction-jobs/{jobId}/failures.csv
GET https://api.gu1.ai/batch-import/transaction-jobs/{jobId}/failures
```

* **`…/failures.csv`** → CSV file download (`Content-Disposition: attachment`)
* **`…/failures`** → JSON body

## Authentication

```bash theme={null}
Authorization: Bearer YOUR_API_KEY
```

Requires at least one of: **`transactions:create`**, **`entities:bulk_import`**, **`events:create`**.

## HTTP responses

| Status | When                                                   |
| ------ | ------------------------------------------------------ |
| `200`  | Job found (CSV download or JSON body)                  |
| `401`  | Not authenticated                                      |
| `403`  | Missing batch-import permission                        |
| `404`  | Unknown `jobId` or job belongs to another organization |

## CSV columns

| Column        | Description                                                                                |
| ------------- | ------------------------------------------------------------------------------------------ |
| `external_id` | Transaction `externalId`                                                                   |
| `code`        | Stable failure code — [catalog](/en/api-reference/bulk-imports/batch-import-failure-codes) |
| `error`       | Human-readable message (same as `message` in JSON)                                         |

Legacy CSV rows (no `code` column) are normalized on download when possible.

## JSON response

<ResponseField name="success" type="boolean">Always `true` when job exists.</ResponseField>

<ResponseField name="jobId" type="string">Batch job id.</ResponseField>

<ResponseField name="kind" type="string">`transaction_batch`</ResponseField>

<ResponseField name="status" type="string">`queued` | `running` | `completed` | `failed` | …</ResponseField>

<ResponseField name="failures" type="array">
  Row failures: `{ externalId, code, message }`. Empty when all rows succeeded or job failed entirely before per-row collection.
</ResponseField>

<ResponseField name="jobFailure" type="object">
  Present when `status=failed` for whole-batch errors (e.g. `INVALID_ENTITY_REFERENCES`): `{ code, message, details? }`.
</ResponseField>

<ResponseField name="truncated" type="boolean">`true` when more than 500 failures exist. The response sample is hydrated from the stored `failures.csv`; download the CSV for the complete list.</ResponseField>

<ResponseField name="failuresTotal" type="number">Total failed rows reported by the job artifact.</ResponseField>

```json theme={null}
{
  "success": true,
  "jobId": "abc-123",
  "kind": "transaction_batch",
  "status": "completed",
  "totalItems": 10,
  "succeeded": 9,
  "failed": 1,
  "skipped": 0,
  "failures": [
    {
      "externalId": "txn-009",
      "code": "CONSTRAINT_VIOLATION",
      "message": "…"
    }
  ],
  "truncated": false,
  "failuresTotal": 1
}
```

<Note>
  With **`batchErrorHandling=rollback_all`** (multipart upload default), an insert error or invalid entity refs (strict validation) aborts the batch: **0 rows created**; ref detail goes to **`failures.csv`** (S3) and `jobFailure`, not a giant metadata array. With **`continue_collect_errors`** or **`stop_keep_success`**, invalid refs and other row failures are recorded per row and valid rows are created (or processing stops at the first failure, depending on policy).
</Note>

## Skipped rows (duplicates)

```
GET https://api.gu1.ai/batch-import/transaction-jobs/{jobId}/skips.csv
```

Rows counted in `skipped` are **not** failures: the transaction was not inserted because another
transaction with the same `externalId` already existed in your organization. This endpoint lists
which ones, so you can tell a duplicate re-upload from a real problem.

| Column        | Description                                   |
| ------------- | --------------------------------------------- |
| `external_id` | Transaction `externalId` that already existed |
| `reason`      | Always `DUPLICATE_EXTERNAL_ID`                |

Same authentication and permissions as the failures endpoints.

| Status | When                                                          |
| ------ | ------------------------------------------------------------- |
| `200`  | CSV download                                                  |
| `404`  | Unknown `jobId`, other organization, or `SKIPS_NOT_AVAILABLE` |

`SKIPS_NOT_AVAILABLE` means the job has no stored report: jobs that finished before this endpoint
existed, jobs with nothing skipped, and jobs sent with `skipDuplicates=false` (duplicates are
resolved by the database and only counted, not listed).

See also: [Failure codes](/en/api-reference/bulk-imports/batch-import-failure-codes), [Create batch transactions](/en/api-reference/transactions/create-batch).
