{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/kawaa-com/json-schema/kawaa-com-batch-accepted-schema.json",
  "title": "BatchAccepted",
  "description": "What `POST /v1/verify/batch` answers with on acceptance. `unique_emails` is what the job will verify; `credits_used` is what was charged, and the two can differ when queueing some addresses could not be confirmed; see the fields themselves.",
  "x-generated": "2026-10-31",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/kawaa-com-openapi.json#/components/schemas/BatchAccepted",
  "type": "object",
  "properties": {
    "job_id": {
      "type": "string",
      "format": "uuid"
    },
    "status": {
      "$ref": "#/$defs/status"
    },
    "total_submitted": {
      "type": "integer",
      "description": "Addresses in the request."
    },
    "unique_emails": {
      "type": "integer",
      "description": "Addresses CONFIRMED queued: what the job will actually verify. It is not always what was charged: when queueing some addresses cannot be confirmed, the API counts what it could confirm and reports the rest in `enqueue_unknown`, so this can be lower than `credits_used` and, in the extreme, 0 on a job that charged. Reconcile billing against `credits_used` and `credits_held`, never against this count."
    },
    "duplicates_removed": {
      "type": "integer",
      "description": "Removed before charging: the same address twice costs once."
    },
    "invalid_emails_skipped": {
      "type": "integer",
      "description": "Rejected as malformed and never verified. The results will be shorter than the list you sent."
    },
    "credits_used": {
      "type": "number",
      "description": "0 on an idempotent replay: what this request charged, not what the batch cost. The batch's cost is its job's `credits_used` less `credits_refunded`, from `GET /v1/jobs/{id}`."
    },
    "credits_remaining": {
      "type": "number",
      "description": "Absent when the post-charge balance could not be safely recovered."
    },
    "idempotent_replay": {
      "type": "boolean",
      "description": "True when this returned an earlier submission's job and charged nothing."
    },
    "dispatch_in_progress": {
      "type": "boolean",
      "description": "A request under this same `Idempotency-Key` is still queueing the batch. Nothing more was charged. Retry after `Retry-After`, or poll the job."
    },
    "enqueue_unknown": {
      "type": "integer",
      "description": "How many sends could not be confirmed. Credits for them are HELD, not spent. Poll the job until it reconciles."
    },
    "credits_held": {
      "type": "number",
      "description": "Held pending reconciliation of an ambiguous enqueue. Not a settled charge."
    },
    "include_activity": {
      "type": "boolean"
    },
    "message": {
      "type": "string"
    },
    "estimated_time_seconds": {
      "type": "integer",
      "description": "A rough estimate of how long the job will take, at about two seconds per address. An estimate for a progress bar, not a deadline. Poll `GET /v1/jobs/{job_id}` for the truth."
    },
    "enqueue_failures": {
      "type": "integer",
      "description": "How many addresses the queue DEFINITIVELY rejected, present only when some were. Distinct from `enqueue_unknown`, which is \"we do not know\". Their credits are refunded where the refund succeeds; where it does not they stay held and `message` says so, so this number beside an unchanged `credits_used` is the case to reconcile rather than to retry."
    }
  },
  "required": [
    "job_id",
    "status",
    "total_submitted",
    "unique_emails",
    "credits_used"
  ]
}