AbuseZero

Imports

Bring benefits you granted before AbuseZero into your claim history, as JSON or as a validated CSV upload.

Imports load the benefits you granted before AbuseZero into your workspace's claim history, so protection starts with your full history on day one. Every imported outcome becomes history that later evaluations are checked against.

There are two ways in:

  • JSON: send up to 1,000 granted outcomes in one request. They're queued and applied in the background, with no review step.
  • CSV: upload up to 100,000 rows. AbuseZero validates every row and holds the import for your review. Nothing is applied until you confirm it.

For a walkthrough, see Import outcome history.

Examples on this page use a server client:

import { AbuseZero } from "abusezero/server";

const abusezero = new AbuseZero({ key: process.env.ABUSEZERO_SECRET_KEY!, outage: "throw" });

The import object

{
  "object": "import",
  "id": "imp_2b8e4d6f-1a3c-4f5e-9b7d-8c0a2e4f6b13",
  "state": "preview",
  "failure": null,
  "total": 1240,
  "validated": 1240,
  "processed": 0,
  "imported": 0,
  "skipped": 0,
  "rejected": 2,
  "created_at": "2026-10-29T11:20:05.000Z",
  "completed_at": null,
  "errors": [
    { "row": 18, "error": "Check this email address." },
    { "row": 907, "error": "Use the time the benefit was actually granted, not a future time." }
  ]
}
FieldTypeDescription
object"import"The object type.
idstringThe import ID, starting with imp_.
statestringWhere the import is. See Import states.
failureobject or nullWhy the import failed, with a code and a message. Present when state is failed, otherwise null. See Failure reasons.
totalintegerRows or outcomes in the import. For a CSV, this is known once validation finishes.
validatedintegerCSV rows checked so far.
processedintegerRows finished so far: imported, skipped and rejected together.
importedintegerRows added to your claim history.
skippedintegerRows that matched history your workspace already had.
rejectedintegerRows that couldn't be imported.
created_atstringWhen the import was created.
completed_atstring or nullWhen the import finished, failed or was cancelled.
errorsarrayUp to the first 100 row problems, each with row (the row number) and error (what to fix). Returned when you retrieve or validate one import, not in lists. The error report has every row.

Import states

StateMeaning
uploadingThe CSV import is waiting for its file and the validate call.
validatingAbuseZero is checking every row.
previewValidation finished. Review the counts and errors, then confirm or cancel.
queuedThe import was accepted and is waiting to be applied.
processingRows are being applied.
completeEvery row was applied. The counts are final.
failedThe import couldn't finish. failure says why. Fix the file, then upload it to a new import.
cancelledThe import was cancelled. Its uploaded file and unfinished rows were deleted.

Failure reasons

failure.codefailure.message
file_not_uploadedThe CSV file wasn't uploaded. Create a new import and upload the file again.
file_too_largeThe CSV is over the 25 MiB limit. Split it into smaller files and import each one.
row_limitThe CSV needs 1 to 100,000 rows. Check the file, then upload it to a new import.
csv_columnsUse unique CSV columns and include email, benefit_key, outcome, and occurred_at.
csv_unreadableThe CSV could not be parsed. Check quoting, columns, and row size, then upload a new file.
file_changedThe uploaded file changed during validation. Create a new import.
import_failedAbuseZero couldn't finish this import. Create a new import and upload the file again.

New codes may be added within v1. Treat a code you don't recognize like import_failed.

Import outcomes as JSON

Adds up to 1,000 granted outcomes in one request. The import is queued and applied in the background. Retrieve it to follow its progress.

MethodPathScopeIdempotency-Key
POST/v1/importsimports:writeRequired

Also available as abusezero imports create in the AbuseZero CLI, abusezero_create_outcome_import on the AbuseZero MCP server and importOutcomes() in the AbuseZero Node.js SDK.

Request body

The body is at most 1 MiB.

FieldTypeRequiredLimitsDescription
outcomesarray of objectsYes1 to 1,000The outcomes to import.
outcomes[].outcome"granted"YesThe benefit was granted.
outcomes[].benefitstringYesA benefit key in this workspaceThe benefit that was granted, such as pro_trial. Create the benefit first.
outcomes[].subject.emailstringYesUp to 254 charactersThe user's email address.
outcomes[].subject.external_idstringNo1 to 128 charactersYour ID for the user. Send it whenever you have it.
outcomes[].subject.email_verifiedbooleanNoWhether your app verified the email address.
outcomes[].occurred_atstringYesISO 8601 with a timezone, not in the futureWhen the benefit was granted.
outcomes[].external_outcome_idstringNo1 to 128 printable characters, no spacesYour ID for this grant. Importing the same ID again doesn't create a second record.
outcomes[].claim_idstringNo1 to 128 printable characters, no spacesThe claim_id you used for this grant in an evaluation, if there was one.

Example

const job = await abusezero.importOutcomes(
  [
    {
      outcome: "granted",
      benefit: "pro_trial",
      subject: { external_id: "user_4821", email: "jordan@example.com", email_verified: true },
      occurred_at: "2026-03-14T09:30:00Z",
      external_outcome_id: "grant_10293",
    },
  ],
  "history-batch-0001",
);

Response

Returns 202.

{
  "object": "import",
  "id": "imp_5f1c7a3e-9d2b-4c6a-8e4f-1b3d5a7c9e20",
  "state": "queued"
}

Rows that can't be applied, for example because their benefit doesn't exist, are counted in rejected with a message in errors. The rest of the import still goes through.

Errors

CodeStatusMeaning
INVALID_FIELD422A field is missing or outside its limits. field names it.
UNSUPPORTED_FIELD422The body has a field the import doesn't define. field names it.
PAYLOAD_TOO_LARGE413The body is over 1 MiB. Split the outcomes into smaller requests.
DELETION_IN_PROGRESS409A data deletion is running in this workspace. Wait for it to finish, then import.
RATE_LIMITED429Too many import requests. Wait for the number of seconds in Retry-After.

For every other code, see REST API errors.

Import a CSV file

A CSV import takes five steps:

  1. Create an upload to get a one-time upload URL.
  2. Upload the file to that URL.
  3. Validate the import. AbuseZero checks every row.
  4. Retrieve the import until it reaches preview, then review its counts and errors.
  5. Confirm the import to apply it, or cancel it.

File format

The first row names the columns. Each column may appear once, and no other columns are accepted.

ColumnRequiredDescription
emailYesThe user's email address.
benefit_keyYesThe benefit that was granted. Create the benefit first.
outcomeYesAlways granted.
occurred_atYesWhen the benefit was granted, as ISO 8601 with a timezone.
external_user_idNoYour ID for the user, 1 to 128 characters.
external_outcome_idNoYour ID for this grant, 1 to 128 printable characters with no spaces.
email,external_user_id,benefit_key,outcome,occurred_at,external_outcome_id
jordan@example.com,user_4821,pro_trial,granted,2026-03-14T09:30:00Z,grant_10293
sam@example.com,user_5307,pro_trial,granted,2026-04-02T16:05:00+02:00,grant_10377

A file holds 1 to 100,000 rows and is at most 25 MiB.

Create an upload

Creates a CSV import and returns the address to upload its file to.

MethodPathScopeIdempotency-Key
POST/v1/imports/csvimports:writeRequired

Also available as abusezero imports create-csv in the AbuseZero CLI, abusezero_create_csv_import on the AbuseZero MCP server and createCsvImport() in the AbuseZero Node.js SDK.

The request has no fields. Send an empty JSON object.

Example

const upload = await abusezero.createCsvImport("history-csv-2026-10-29") as {
  id: string;
  url: string;
  upload_headers: Record<string, string>;
};

Response

Returns 202.

{
  "object": "import_upload",
  "id": "imp_2b8e4d6f-1a3c-4f5e-9b7d-8c0a2e4f6b13",
  "url": "https://api.abusezero.com/…",
  "upload_headers": {
    "Content-Type": "text/csv",
    "If-None-Match": "*"
  }
}
FieldTypeDescription
idstringThe new import's ID.
urlstringWhere to upload the file. It works for 5 minutes and accepts one upload.
upload_headersobjectHeaders to send with the upload, exactly as given.

A retry with the same Idempotency-Key returns the same import with a fresh upload url, so a lost response never leaves an extra import behind.

Errors

CodeStatusMeaning
DELETION_IN_PROGRESS409A data deletion is running in this workspace. Wait for it to finish, then import.

Upload the file

Send the file to url with a PUT request and every header in upload_headers. No API key is needed: the URL carries its own authorization. Send the whole file as one request body, as both samples do.

import { readFile } from "node:fs/promises";

const response = await fetch(upload.url, {
  method: "PUT",
  headers: upload.upload_headers,
  body: await readFile("history.csv"),
});
if (!response.ok) throw new Error(`Upload failed with status ${response.status}`);

A successful upload returns 204 with no body. A failed upload returns the standard error envelope:

CodeStatusMeaning
UPLOAD_HEADERS400The request didn't send the headers in upload_headers exactly.
UPLOAD_EMPTY400The request had no file.
IMPORT_SIZE413The file is larger than 25 MiB. Split it into smaller files.
TRANSFER_EXPIRED403The 5 minutes have passed. Create a new upload.
INVALID_TRANSFER403The URL was changed or isn't complete. Use it exactly as returned.
UPLOAD_EXISTS409A file was already uploaded to this import. Create a new import to upload another file.

Validate the import

Starts validation of the uploaded file. Call it once the upload succeeds.

MethodPathScopeIdempotency-Key
POST/v1/imports/{id}/validateimports:writeRequired

Also available as abusezero imports validate in the AbuseZero CLI, abusezero_validate_import on the AbuseZero MCP server and validateImport() in the AbuseZero Node.js SDK.

Path parameters

ParameterTypeDescription
idstringThe import ID, starting with imp_.

Example

const job = await abusezero.validateImport(upload.id, "validate-imp-2b8e4d6f");

Response

Returns 201 with the import object, usually in validating. Calling it again after validation has started returns the import as it is.

Retrieve the import until it reaches preview or failed. A file fails validation when its columns don't match the file format, it has no rows or more than 100,000, it's larger than 25 MiB, it can't be read as CSV, or it changed during validation.

Errors

CodeStatusMeaning
IMPORT_NOT_FOUND404No import with this ID exists in the workspace.
IMPORT_NOT_UPLOADED409The CSV file hasn't been uploaded yet. The import stays in uploading; upload the file, then validate again.

Retrieve an import

MethodPathScopeIdempotency-Key
GET/v1/imports/{id}imports:readNot used

Also available as abusezero imports get in the AbuseZero CLI, abusezero_get_import on the AbuseZero MCP server and getImport() in the AbuseZero Node.js SDK.

Path parameters

ParameterTypeDescription
idstringThe import ID, starting with imp_.

Example

const job = await abusezero.getImport("imp_2b8e4d6f-1a3c-4f5e-9b7d-8c0a2e4f6b13");

Response

Returns 200 with the import object, including up to the first 100 row errors.

Errors

CodeStatusMeaning
IMPORT_NOT_FOUND404No import with this ID exists in the workspace.

Download the error report

Creates a CSV of every row problem in an import, with the columns row and error.

MethodPathScopeIdempotency-Key
GET/v1/imports/{id}/errorsimports:readNot used

Also available as abusezero imports errors in the AbuseZero CLI, abusezero_get_import_errors on the AbuseZero MCP server and call() in the AbuseZero Node.js SDK.

Path parameters

ParameterTypeDescription
idstringThe import ID, starting with imp_.

Example

const report = await abusezero.call("/v1/imports/imp_2b8e4d6f-1a3c-4f5e-9b7d-8c0a2e4f6b13/errors");

Response

Returns 200. The link works for 5 minutes. Request the report again for a new link.

{
  "object": "import_error_report",
  "url": "https://api.abusezero.com/…"
}

Errors

CodeStatusMeaning
IMPORT_NOT_FOUND404No import with this ID exists in the workspace.
IMPORT_REPORT_EXPIRED410Error reports are available until 7 days after an import finishes. Upload the corrected CSV in a new import.

Confirm the import

Applies an import that's in preview. Rows that were rejected during validation aren't applied.

MethodPathScopeIdempotency-Key
POST/v1/imports/{id}/confirmimports:writeRequired

Also available as abusezero imports confirm in the AbuseZero CLI, abusezero_confirm_import on the AbuseZero MCP server and confirmImport() in the AbuseZero Node.js SDK.

Path parameters

ParameterTypeDescription
idstringThe import ID, starting with imp_.

Example

await abusezero.confirmImport("imp_2b8e4d6f-1a3c-4f5e-9b7d-8c0a2e4f6b13", "confirm-imp-2b8e4d6f");

Response

Returns 201.

{
  "object": "import",
  "id": "imp_2b8e4d6f-1a3c-4f5e-9b7d-8c0a2e4f6b13",
  "state": "queued"
}

Errors

CodeStatusMeaning
IMPORT_STATE409The import isn't in preview. Validate it and review it before confirming.

Cancel an import

Cancelling is permanent. AbuseZero stops the import and deletes its uploaded file and every row that hasn't been applied yet. Rows already imported stay in your claim history as outcomes.

MethodPathScopeIdempotency-Key
POST/v1/imports/{id}/cancelimports:writeRequired

Also available as abusezero imports cancel in the AbuseZero CLI, abusezero_cancel_import on the AbuseZero MCP server and cancelImport() in the AbuseZero Node.js SDK.

Path parameters

ParameterTypeDescription
idstringThe import ID, starting with imp_.

Example

await abusezero.cancelImport("imp_2b8e4d6f-1a3c-4f5e-9b7d-8c0a2e4f6b13", "cancel-imp-2b8e4d6f");

Response

Returns 201.

{
  "object": "import",
  "id": "imp_2b8e4d6f-1a3c-4f5e-9b7d-8c0a2e4f6b13",
  "state": "cancelled"
}

Errors

CodeStatusMeaning
IMPORT_NOT_FOUND404No import with this ID exists in the workspace.
IMPORT_COMPLETE409The import already finished, so it can't be cancelled. To remove its history, delete the affected users' data.

List imports

Returns imports newest first. List items don't include errors.

MethodPathScopeIdempotency-Key
GET/v1/importsimports:readNot used

Also available as abusezero imports list in the AbuseZero CLI, abusezero_list_imports on the AbuseZero MCP server and imports() in the AbuseZero Node.js SDK.

Query parameters

ParameterTypeRequiredLimitsDescription
limitintegerNo1 to 100, default 50How many imports to return.
cursorstringNoThe next_cursor from the previous page, unchanged.

Example

const page = await abusezero.imports({ limit: 20 });

Response

Returns 200.

{
  "object": "list",
  "data": [
    {
      "object": "import",
      "id": "imp_2b8e4d6f-1a3c-4f5e-9b7d-8c0a2e4f6b13",
      "state": "complete",
      "total": 1240,
      "validated": 1240,
      "processed": 1240,
      "imported": 1191,
      "skipped": 47,
      "rejected": 2,
      "created_at": "2026-10-29T11:20:05.000Z",
      "completed_at": "2026-10-29T11:26:48.000Z"
    }
  ],
  "next_cursor": null
}

How is this guide?

Last updated on

On this page