Skip to main content
Contract preview · not generally available

BankScanPro API developer preview

This is the proposed v1 contract for accepted private-pilot customers. It documents the integration shape used for pilot planning; it is not a promise that self-serve credentials or every endpoint are currently live.

Status

Private pilot contract

The contract is intentionally versioned before general availability. Accepted pilot customers receive a pinned contract, credentials, allowance, support channel, and acceptance criteria.

Contract version
0.9.0-preview
Base URL
https://api.bankscanpro.com/v1
Authentication
Bearer API key
Usage metric
Physical PDF pages

Quickstart

Submit one PDF as an asynchronous job

Use a non-sensitive idempotency key so a network retry cannot create duplicate billable work.

curl
curl https://api.bankscanpro.com/v1/jobs \
  --request POST \
  --header "Authorization: Bearer $BANKSCANPRO_API_KEY" \
  --header "Idempotency-Key: loan-application-1842" \
  --form "file=@statement.pdf" \
  --form 'options={"pages":"all","result_format":"canonical_json"}'
JavaScript
const form = new FormData();
form.append("file", statementPdf);
form.append("client_reference", "loan-application-1842");
form.append("pages", "all");
form.append("result_format", "canonical_json");

const response = await fetch("https://api.bankscanpro.com/v1/jobs", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BANKSCANPRO_API_KEY}`,
    "Idempotency-Key": "loan-application-1842",
  },
  body: form,
});

const job = await response.json();

Authentication

Keep API keys on your server

Pilot credentials are scoped to one workspace and environment. Never expose them in browser code, mobile apps, statement filenames, analytics properties, or support screenshots.

Environment separation

Test and production credentials are issued and revoked independently.

Rotation

Applications should support overlapping keys during a controlled rotation window.

Log hygiene

Log request IDs and job IDs, never raw keys or financial document content.

Jobs

A small resource model

The v1 preview keeps ingestion, status, result retrieval, approved exports, cancellation, and usage reporting explicit.

MethodPathPurpose
POST/jobsUpload a PDF and create a conversion job
GET/jobs/{job_id}Read processing or terminal status
GET/jobs/{job_id}/resultRetrieve a deliverable canonical result
POST/jobs/{job_id}/exportsCreate a format allowed by the delivery decision
DELETE/jobs/{job_id}Cancel running work or request artifact deletion
GET/usageRead allowance and page consumption

Results

Treat delivery state as part of the data contract

A successful HTTP request does not mean the result is automatically complete. Your integration must branch on the terminal job status and delivery object.

complete

The requested page scope passed the automatic delivery gates. Review signals remain available.

partial

Source-backed rows are available, but the supplied document or requested scope is incomplete.

needs_review

The result is retained for review instead of being silently promoted to a reliable conversion.

failed

No safe result is available. The error response explains whether the request can be retried.

Recommended integration rule

Only automate downstream accounting or underwriting actions after checking `status`, `delivery.automatic_complete`, `delivery.allowed_exports`, and any workflow-specific validation requirement.

Webhooks

Verify, deduplicate, then acknowledge

The preview sends terminal job events. Polling remains the recovery mechanism if a webhook is delayed or rejected.

Verify the raw body using the timestamp and signature headers before parsing JSON.

Reject stale timestamps and store event IDs so retries are processed idempotently.

Return a fast 2xx response, then perform downstream work asynchronously.

webhook verification sketch
const timestamp = request.headers.get("BankScanPro-Timestamp");
const signature = request.headers.get("BankScanPro-Signature");
const signedPayload = `${timestamp}.${rawRequestBody}`;

verifyHmacSha256({
  secret: process.env.BANKSCANPRO_WEBHOOK_SECRET,
  payload: signedPayload,
  signature,
});

// Reject stale timestamps, then process event.id idempotently.

Errors

Retry only when the response says it is safe

Every error includes a stable code, human-readable message, retryable flag, and request ID. Do not infer retryability from the HTTP status alone.

error response
{
  "type": "api_error",
  "code": "result_not_ready",
  "message": "The job has not reached a deliverable terminal state.",
  "retryable": true,
  "request_id": "req_01JZ7M2V75B2D8XQ6M5S9F1J3C",
  "details": {
    "job_id": "job_01JZ7M2V6VB9K0Y5F3Q1W8A2DN",
    "status": "processing"
  }
}

Limits & usage

Pilot limits are contractual, not universal defaults

File size, pages per job, concurrency, requests per second, result retention, and webhook retry windows are defined in the accepted pilot plan.

Billing unit

Physical PDF pages accepted for processing

Reservation

Pages may be reserved at job creation and settled at terminal state

Rate response

429 with Retry-After when a workspace limit is reached

Usage endpoint

Included, used, remaining, and overage page counts

OpenAPI

Preview schema 0.9.0-preview

Use the machine-readable contract for review and client generation experiments, but pin the version supplied in your pilot agreement.

openapi.json
{
  "openapi": "3.1.0",
  "info": {
    "title": "BankScanPro API",
    "version": "0.9.0-preview",
    "summary": "Private pilot contract for bank statement conversion",
    "description": "Preview contract for accepted BankScanPro API pilot customers. The API is not generally available and this contract may change before v1 general availability.",
    "contact": {
      "name": "BankScanPro API Pilot",
      "url": "https://bankscanpro.com/contact/api"
    }
  },
  "servers": [
    {
      "url": "https://api.bankscanpro.com/v1",
      "description": "Reserved production hostname for accepted pilot customers"
    }
  ],
  "tags": [
    {
      "name": "Jobs",
      "description": "Submit and inspect asynchronous conversion jobs."
    },
    {
      "name": "Exports",
      "description": "Create approved file exports from a deliverable result."
    },
    {
      "name": "Usage",
      "description": "Inspect the current API allowance and consumption."
    }
  ],
  "paths": {
    "/jobs": {
      "post": {
        "tags": [
          "Jobs"
        ],
        "operationId": "createStatementConversionJob",
        "summary": "Submit a bank statement conversion job",
        "description": "Creates an asynchronous job from one digital or scanned PDF. Repeating a request with the same idempotency key returns the original job.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "A unique, non-sensitive key for safely retrying this submission.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 160
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "One PDF bank or credit-card statement."
                  },
                  "client_reference": {
                    "type": "string",
                    "maxLength": 160,
                    "description": "Your non-sensitive identifier for correlating the job."
                  },
                  "pages": {
                    "oneOf": [
                      {
                        "type": "string",
                        "const": "all"
                      },
                      {
                        "type": "array",
                        "items": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "minItems": 1
                      }
                    ],
                    "default": "all"
                  },
                  "result_format": {
                    "type": "string",
                    "enum": [
                      "canonical_json"
                    ],
                    "default": "canonical_json"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/AllowanceRequired"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/jobs/{job_id}": {
      "get": {
        "tags": [
          "Jobs"
        ],
        "operationId": "getStatementConversionJob",
        "summary": "Get job status",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JobId"
          }
        ],
        "responses": {
          "200": {
            "description": "Current job state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Jobs"
        ],
        "operationId": "cancelOrDeleteStatementConversionJob",
        "summary": "Cancel a running job or request artifact deletion",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JobId"
          }
        ],
        "responses": {
          "202": {
            "description": "Cancellation or deletion request accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "id",
                    "status"
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "cancelling",
                        "deletion_requested"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/jobs/{job_id}/result": {
      "get": {
        "tags": [
          "Jobs"
        ],
        "operationId": "getStatementConversionResult",
        "summary": "Get the canonical result",
        "description": "Available when the job is complete, partial, or needs_review.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JobId"
          }
        ],
        "responses": {
          "200": {
            "description": "Canonical statement result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatementResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/ResultNotReady"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/jobs/{job_id}/exports": {
      "post": {
        "tags": [
          "Exports"
        ],
        "operationId": "createStatementExport",
        "summary": "Create an allowed file export",
        "description": "The requested format must appear in delivery.allowed_exports.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JobId"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 160
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "format"
                ],
                "properties": {
                  "format": {
                    "type": "string",
                    "enum": [
                      "csv",
                      "excel",
                      "qbo",
                      "ofx"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Export created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Export"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/ExportNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/usage": {
      "get": {
        "tags": [
          "Usage"
        ],
        "operationId": "getApiUsage",
        "summary": "Get current page allowance and usage",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Current usage period",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "webhooks": {
    "jobTerminal": {
      "post": {
        "operationId": "jobTerminalWebhook",
        "summary": "A job reached a terminal state",
        "description": "Sent for complete, partial, needs_review, failed, or cancelled jobs. Consumers verify the BankScanPro-Signature header.",
        "parameters": [
          {
            "name": "BankScanPro-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook accepted"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "BankScanPro API key"
      }
    },
    "parameters": {
      "JobId": {
        "name": "job_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^job_[A-Za-z0-9]+$"
        }
      }
    },
    "schemas": {
      "JobStatus": {
        "type": "string",
        "enum": [
          "queued",
          "processing",
          "complete",
          "partial",
          "needs_review",
          "failed",
          "cancelled"
        ]
      },
      "Job": {
        "type": "object",
        "required": [
          "id",
          "object",
          "status",
          "created_at",
          "request_id"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "job_01JZ7M2V6VB9K0Y5F3Q1W8A2DN"
            ]
          },
          "object": {
            "type": "string",
            "const": "statement_conversion_job"
          },
          "status": {
            "$ref": "#/components/schemas/JobStatus"
          },
          "progress": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 100
          },
          "client_reference": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "result_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri-reference"
          },
          "error": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Error"
              },
              {
                "type": "null"
              }
            ]
          },
          "request_id": {
            "type": "string"
          }
        }
      },
      "StatementResult": {
        "type": "object",
        "required": [
          "id",
          "status",
          "page_scope",
          "statement",
          "validation",
          "delivery",
          "request_id"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/JobStatus"
          },
          "page_scope": {
            "type": "object",
            "required": [
              "source_document_total_pages",
              "requested_pages",
              "delivery_pages",
              "document_completeness"
            ],
            "properties": {
              "source_document_total_pages": {
                "type": "integer",
                "minimum": 1
              },
              "requested_pages": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "delivery_pages": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "document_completeness": {
                "type": "string",
                "enum": [
                  "full_document",
                  "user_selected_subset",
                  "pipeline_incomplete"
                ]
              }
            }
          },
          "statement": {
            "type": "object",
            "required": [
              "currency",
              "period",
              "transactions"
            ],
            "properties": {
              "institution_name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "account_type": {
                "type": "string",
                "enum": [
                  "checking",
                  "savings",
                  "credit_card",
                  "loan",
                  "unknown"
                ]
              },
              "currency": {
                "type": "string",
                "minLength": 3,
                "maxLength": 3
              },
              "period": {
                "type": "object",
                "properties": {
                  "start_date": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date"
                  },
                  "end_date": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date"
                  },
                  "opening_balance": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "closing_balance": {
                    "type": [
                      "number",
                      "null"
                    ]
                  }
                }
              },
              "transactions": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "validation": {
            "type": "object",
            "additionalProperties": true,
            "description": "Named validation checks and warning identifiers used by the BASE delivery contract."
          },
          "delivery": {
            "type": "object",
            "required": [
              "automatic_complete",
              "allowed_exports",
              "blocking_reasons",
              "review_reasons"
            ],
            "properties": {
              "automatic_complete": {
                "type": "boolean"
              },
              "allowed_exports": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "csv",
                    "excel",
                    "qbo",
                    "ofx"
                  ]
                }
              },
              "blocking_reasons": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "review_reasons": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "request_id": {
            "type": "string"
          }
        }
      },
      "Transaction": {
        "type": "object",
        "required": [
          "id",
          "date",
          "description",
          "debit",
          "credit",
          "balance",
          "currency",
          "source"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "description": {
            "type": "string"
          },
          "debit": {
            "type": [
              "number",
              "null"
            ]
          },
          "credit": {
            "type": [
              "number",
              "null"
            ]
          },
          "balance": {
            "type": [
              "number",
              "null"
            ]
          },
          "currency": {
            "type": "string"
          },
          "reference": {
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "type": "object",
            "required": [
              "page_number",
              "line_ids"
            ],
            "properties": {
              "page_number": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 1
              },
              "line_ids": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "Export": {
        "type": "object",
        "required": [
          "id",
          "format",
          "download_url",
          "expires_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "format": {
            "type": "string",
            "enum": [
              "csv",
              "excel",
              "qbo",
              "ofx"
            ]
          },
          "download_url": {
            "type": "string",
            "format": "uri"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Usage": {
        "type": "object",
        "required": [
          "period_start",
          "period_end",
          "included_pages",
          "used_pages",
          "remaining_pages"
        ],
        "properties": {
          "period_start": {
            "type": "string",
            "format": "date-time"
          },
          "period_end": {
            "type": "string",
            "format": "date-time"
          },
          "included_pages": {
            "type": "integer",
            "minimum": 0
          },
          "used_pages": {
            "type": "integer",
            "minimum": 0
          },
          "remaining_pages": {
            "type": "integer",
            "minimum": 0
          },
          "overage_pages": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "required": [
          "id",
          "type",
          "created_at",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "job.complete",
              "job.partial",
              "job.needs_review",
              "job.failed",
              "job.cancelled"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "required": [
              "job"
            ],
            "properties": {
              "job": {
                "$ref": "#/components/schemas/Job"
              }
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "type",
          "code",
          "message",
          "retryable",
          "request_id"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "api_error"
          },
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "retryable": {
            "type": "boolean"
          },
          "request_id": {
            "type": "string"
          },
          "details": {
            "type": "object",
            "additionalProperties": true
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request could not be validated",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "The API key is missing, invalid, or revoked",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "AllowanceRequired": {
        "description": "The workspace does not have sufficient page allowance",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "The requested resource was not found",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "The request conflicts with the current resource state",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ResultNotReady": {
        "description": "The job has not reached a deliverable terminal state",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ExportNotAllowed": {
        "description": "The validation or page-scope decision does not allow this export",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "The PDF exceeds the pilot file or page limit",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "The workspace exceeded its current request limit",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  },
  "x-bankscanpro-status": "private_pilot_preview",
  "x-bankscanpro-generated": "2026-07-16"
}

Changelog

Contract history

0.9.0-preview

Initial private-pilot preview covering jobs, canonical results, fail-closed delivery states, exports, usage, deletion, errors, and signed terminal webhooks.

Apply for API Pilot →Sample JSON