End Close Records API
The Records API from End Close — 3 operation(s) for records.
The Records API from End Close — 3 operation(s) for records.
openapi: 3.1.0
info:
title: End Close Bank Account Balances Records API
description: REST API is used to interact with the End Close platform.
license:
name: MIT
version: 1.0.0
servers:
- url: https://api.endclose.com/v1
security:
- ApiKeyAuth: []
tags:
- name: Records
paths:
/records:
get:
summary: List records
description: 'Returns a paginated list of records, newest first. At least one of `data_stream_key`, `reconciliation_match_id`, `reconciliation_id`, `external_id`, `import_batch_id`, or `bulk_request_id` is required.
Pagination is cursor-based: pass each response''s `next_cursor` back as `cursor` until `next_cursor` is null. The legacy `offset` parameter and its `has_more`/`offset`/`total_count` response shape have been removed.'
parameters:
- in: query
name: data_stream_key
required: false
schema:
type: string
description: Filter by data stream key
- in: query
name: reconciliation_match_id
required: false
schema:
type: string
description: Filter to records that belong to the given match
- in: query
name: reconciliation_id
required: false
schema:
type: string
description: Filter to records that have a status row in the given reconciliation
- in: query
name: external_id
required: false
schema:
type: string
description: Filter to records with this exact `external_id`
- in: query
name: import_batch_id
required: false
schema:
type: integer
description: Filter to records created by this import batch
- in: query
name: bulk_request_id
required: false
schema:
type: integer
description: Filter to records created by this bulk request
- in: query
name: created_after
required: false
schema:
type: string
format: date-time
description: ISO8601 lower bound on `created_at`
- in: query
name: created_before
required: false
schema:
type: string
format: date-time
description: ISO8601 upper bound on `created_at`
- in: query
name: source
required: false
schema:
type: string
enum:
- csv_upload
- nacha_upload
- bulk_api
description: Filter via the joined import batch's `source`
- in: query
name: status
required: false
schema:
type: string
enum:
- matched
- unmatched
description: Filter by match status
- in: query
name: cursor
required: false
schema:
type: string
description: Opaque cursor from a previous response's `next_cursor`. Omit for the first page.
- in: query
name: limit
required: false
schema:
type: integer
minimum: 1
maximum: 200
default: 50
responses:
'200':
description: Paginated list of records
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/Record'
next_cursor:
type: string
nullable: true
description: Opaque cursor for the next page. Null when no more pages.
limit:
type: integer
'422':
description: No scoping filter provided, or invalid `status`, `created_after`, or `created_before`
tags:
- Records
post:
summary: Create a record
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Record'
responses:
'201':
description: Record created
content:
application/json:
schema:
$ref: '#/components/schemas/Record'
tags:
- Records
/records/bulk:
post:
summary: Bulk create records
description: Submit up to 1,000 records for asynchronous processing. Returns immediately with a bulk request ID. Each record is validated and inserted independently, so some records can succeed while others fail. Use the `GET /bulk_requests/{id}` endpoint to poll for results.
parameters:
- in: header
name: Idempotency-Key
schema:
type: string
description: Optional idempotency key. If a bulk request with this key already exists for your environment, the existing request is returned instead of creating a new one.
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- records
properties:
on_conflict:
type: string
enum:
- skip
- error
default: skip
description: How to handle records whose `external_id` already exists in the target stream. `skip` (default) silently skips duplicates and counts them in `skipped_items`. `error` surfaces duplicates as per-row failures. Only applies to records with `external_id` set.
records:
type: array
minItems: 1
maxItems: 1000
items:
type: object
required:
- date
- data_stream_key
- amount
- direction
properties:
date:
type: string
format: date
description: The date of the record
data_stream_key:
type: string
description: Key of the data stream this record belongs to
amount:
type: integer
description: The monetary amount in cents (e.g., 1234 = $12.34)
currency:
type: string
description: Three-letter ISO 4217 currency code (e.g. USD, EUR). **Optional — defaults to `USD` when omitted.** Case-insensitive on input.
pattern: ^[A-Za-z]{3}$
default: USD
direction:
type: string
enum:
- credit
- debit
description: Whether this is a credit or debit
description:
type: string
description: Optional description
external_id:
type: string
description: Optional external identifier
metadata:
type: object
additionalProperties: true
default: {}
description: Arbitrary key-value metadata
responses:
'202':
description: Bulk request accepted for processing
content:
application/json:
schema:
$ref: '#/components/schemas/BulkRequestSummary'
'400':
description: Invalid `on_conflict` value
content:
application/json:
schema:
type: object
properties:
message:
type: string
'401':
description: Unauthorized
'422':
description: Invalid request (e.g. records is not an array, empty, or exceeds 1,000 items)
content:
application/json:
schema:
type: object
properties:
message:
type: string
tags:
- Records
/records/{id}:
get:
summary: Retrieve a record
description: Retrieve a single record by its unique ID.
parameters:
- in: path
name: id
required: true
schema:
type: string
description: The unique identifier of the record
responses:
'200':
description: Record retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Record'
'401':
description: Unauthorized
'404':
description: Record not found
tags:
- Records
patch:
summary: Update a record
description: Update a record's fields. The record **must have a status of `unreconciled`**. Records that are partially or fully reconciled cannot be modified.
parameters:
- in: path
name: id
required: true
schema:
type: string
description: The unique identifier of the record
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
amount:
type: integer
description: The monetary amount in cents (e.g., 1234 = $12.34)
direction:
type: string
enum:
- credit
- debit
description:
type: string
date:
type: string
format: date
external_id:
type: string
metadata:
type: object
additionalProperties: true
responses:
'200':
description: Record updated
content:
application/json:
schema:
$ref: '#/components/schemas/Record'
'401':
description: Unauthorized
'404':
description: Record not found
'409':
description: Record cannot be modified because it has already been reconciled
tags:
- Records
delete:
summary: Delete a record
description: Deletes a single record by its ID. The record **must have a status of `unreconciled`**. Records that are partially or fully reconciled cannot be deleted.
parameters:
- in: path
name: id
required: true
schema:
type: string
description: The unique identifier of the record
responses:
'204':
description: Record deleted successfully
'401':
description: Unauthorized
'404':
description: Record not found
'409':
description: Record cannot be deleted because it is already reconciled or partially reconciled
tags:
- Records
components:
schemas:
BulkRequestSummary:
type: object
description: Summary returned when a bulk request is created or retrieved via idempotency key
properties:
id:
type: integer
description: Unique identifier for the bulk request
status:
type: string
enum:
- pending
- processing
- completed
- failed
- cancelled
description: Current processing status
total_items:
type: integer
description: Number of records submitted
processed_items:
type: integer
description: Number of records processed so far
successful_items:
type: integer
description: Number of records successfully created
failed_items:
type: integer
description: Number of records that failed
skipped_items:
type: integer
description: 'Number of records skipped due to `on_conflict: skip` matching an existing `external_id`'
url:
type: string
description: Polling URL to check progress (e.g. /v1/bulk_requests/123)
Record:
type: object
required:
- date
- data_stream_key
- amount
- direction
properties:
id:
type: integer
amount:
type: integer
description: The monetary amount in cents (e.g., 1234 = $12.34)
currency:
type: string
description: Three-letter ISO 4217 currency code (e.g. USD, EUR). **Optional on create — defaults to `USD` when omitted.** Case-insensitive on input; always returned uppercase.
pattern: ^[A-Za-z]{3}$
default: USD
decimal_places:
type: integer
description: Number of decimal places for this currency (2 for USD)
default: 2
direction:
type: string
enum:
- credit
- debit
description:
type: string
date:
type: string
format: date
metadata:
type: object
additionalProperties: true
default: {}
external_id:
type: string
data_stream_key:
type: string
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-KEY