Skip to content
Last updated

API changelog

What changed in openapi.yaml, and what you have to do about it.

Read this before you regenerate an SDK. Every published SDK is generated from this spec on every release, so a change here reaches your build whether or not you asked for it.

How versions work during beta

The version lives in info.version and looks like 1.0.0-beta.N.

  • N increases by exactly one on every push that changes the spec. No skips, no reuse, no batching. One push, one N.
  • N carries no meaning beyond identity. It tells you which build you compiled against. It does not tell you whether the change was safe.
  • Severity lives in this file, not in the number. Read the categories below.

At general availability the version drops to 1.0.0 and switches to ordinary semantic versioning, where a major bump means a breaking change.

What the categories mean

CategoryWhat happenedWhat you do
BreakingClient code that was correct stops workingChange your code before this version reaches your environment
DeprecatedStill works, removal is scheduledPlan the work before the stated date
AddedNew optional surfaceAdopt when it is useful to you
CorrectedThe document changed, the API did notRe-read if you built to the old text

Corrected is the category to watch. Much of this spec was written ahead of the service, so a correction often means the API never behaved the way the document claimed. If you built to observed behavior you are fine. If you built to the document, you may have been broken all along and this change is what tells you.

How deprecations work

A deprecation is a promise with a date on it.

  • Every bullet under Deprecated ends with Removal: YYYY-MM-DD. An open-ended deprecation is not permitted, and CI rejects one.
  • The default window is four weeks from the release that announces it. A shorter window needs the affected team's agreement; CI warns rather than fails, so the exception is visible instead of silent.
  • When the deprecated value appears on a response, the old value keeps being returned for the whole window. Generated SDKs throw on a value they do not know, so a client on an older SDK breaks the moment the response changes — not when the enum grows. The announcement and the flip are separate releases, and both appear here.

A note on the entries before beta.23

This file starts at beta.19. Everything from beta.19 through beta.22 is reconstructed from git history, because no changelog existed at the time and the version was not bumped per push — beta.20 alone covers 20 commits across six days. Treat those four entries as a good-faith summary rather than a record, and confirm anything you intend to rely on against the spec itself.

beta.23 onward is written at the time of the change.

1.0.0-beta.27 — 2026-09-18

Documentation only. No request or response changed, and no field moved. One example value contradicted the constraint printed beside it.

Corrected

  • bank_number no longer shows a six-character example against a four-character limit. On the request side the field declares maxLength: 4 and its example read "123456", so anyone who copied the example got a 400 from the gateway. The value left behind when the field was capped for beta.23; the cap moved and the example did not. It now reads "1234", which is what TsysIntegrationRequestExample in this same document has said all along. The nullable twin on the detail schema carries no maxLength, so its example was legal but described a bank number TSYS never issues — it moved to "1234" as well, so the field reads the same everywhere.

    Nothing about the contract changed. If you were sending a four-character bank_number, as the constraint required, you were always correct.

1.0.0-beta.26 — 2026-09-17

Documentation only. No request or response changed, and nothing you built against beta.25 needs touching. Three descriptions were wrong or silent about behavior the gateway has always had, and there is one new example.

Found while rewriting the developer portal's Payments section against this document.

Added

  • A payout example on POST /v1/transactions, request and response. There was no PAYOUT example before, so the only way to see the shape was to read BankAccountInput and infer it. PayoutRequestExample shows an ACH credit with standard_entry_class: CCD; PayoutResponseExample shows the approved result, including the ACH return fields in their not-yet-returned state.

Corrected

  • Tip now says how it relates to amount. It never did, and the two readings differ by the size of the tip. amount is the whole charge and tip declares how much of that charge is gratuity — a $100.00 bill with a $20.00 tip is amount: 12000 with tip: 2000. The tip is reported to the processor separately at settlement and never increases the settled amount. If you were sending the pre-tip figure in amount, you have been under-charging.
  • SaleRecurringSubsequentRequestExample no longer claims its rules are enforced by the schema. They are not: TransactionCreateRequest and RecurringDetails carry flat required lists and no conditional validation, so a generated client accepts a request the gateway then rejects with 400 INVALID_ACTION. The rules are real and the gateway enforces them; only the attribution was wrong.
  • A subsequent recurring payment may carry either initial_transaction_id or initial_network_transaction_id. The same example said the first was required. It is not — the gateway accepts either, and initial_network_transaction_id is the one to send when the agreement began outside this gateway.

1.0.0-beta.25 — 2026-09-16

Payment method changes on both sides of the contract, plus the client request schemas. Most of this is the document catching up with the service, so read the Corrected section first — if you built to the old text rather than to observed behavior, that is where you find out.

This version covers four spec pushes, not one. The version did not move on any of them, so the release check failed each time and no SDK was built. No SDK was ever published from those four commits. Nothing is missing from your build as a result — the SDKs go straight from beta.24 to this version, carrying everything below.

Breaking

  • payment_method on an ACH response replaces name with first_name and last_name. The gateway made this change on 2026-09-11 and this document did not follow, so beta.24 has been describing a field the API stopped sending. BankAccountOutput sets additionalProperties: false, which means a client validating an ACH transaction response against beta.24 has been rejecting every one of them since that date. Read last_name where you read name; it carries the same value the old field did.
  • PAYMENT_TOKEN is gone from PaymentMethodTypeOutput, and PaymentTokenOutput is gone from PaymentMethodOutput. Your generated enum loses a member, so code naming it stops compiling. That code was already dead. The gateway never returned it: paying with a token resolves the token to the card or bank account behind it and reports that, so a response is CARD or ACH and the token comes back in payment_token on either one. Delete the branch rather than replacing it.

Corrected

  • payment_method.card_type is always UNKNOWN on a transaction response. Not sometimes — always. The processor does not return the card type on the authorization, so the gateway has nothing to report yet, and the determined value appears when you read the transaction back from GET /v1/transactions or GET /v1/transactions/{id}. TransactionResponse now states the general rule this is an instance of: the transaction and reporting endpoints return the same object, a transaction response is the earlier of the two, and some values are not known when the gateway answers the original request. Treat UNKNOWN as "not yet known" on a transaction response and "the processor could not determine it" on a reporting response.
  • card_type on GET /v1/transactions accepts a comma-separated list. It always did; the document declared a single value. Send card_type=CREDIT,DEBIT to match either.
  • card_type on GET /v1/batches/transactions is a different parameter with a narrower value set. It takes DEBIT or CREDIT only — UNKNOWN returns 400 there — and it does not accept a list. CREDIT on that operation also returns transactions whose card type is undetermined, because open-batch eligibility groups the two together. The same value therefore selects a wider set of rows there than on GET /v1/transactions. One schema was describing both operations and could not describe either correctly.
  • metadata states the four limits the gateway enforces. At most 50 keys, keys matching ^[a-zA-Z0-9_-]+$ up to 40 characters, values up to 500 characters. All four were already enforced and the 50-key cap was not written down anywhere. They are machine-readable now, so your SDK can check them instead of your reading this paragraph.
  • admin_contact_details on a client request is no longer the same schema as on a response. The request schemas are new: ClientAdminContactDetailsInput on create, where first_name, last_name and email are required, non-nullable and must not be empty, and ClientAdminContactDetailsUpdate on update, where every property is optional. The old shared schema declared those properties required and nullable at once, so sending "first_name": null satisfied the document and returned 400 from the service.

Added

  • A partial client update is now legal in the document. POST /v1/clients/{clientId} has always accepted a body changing only what you name; the shared schema made the document demand four contact properties you had no intention of changing. Send only what you are changing. Re-sending the rest was how a value got overwritten with a stale copy.

Known limitation

  • The response admin_contact_details still declares its four properties required and nullable. That is deliberate and correct on a response: the keys are always present, and a client created through an internal path can genuinely carry null. Only the request side was tightened.
  • format: email on admin_contact_details.email does not describe what is accepted. The gateway enforces a stricter rule than the format implies — it rejects plus-addressing and every top-level domain longer than four characters, so ops+paradise@example.com and ops@acme.agency both return 400. That is a service defect rather than a documentation one, and it is being fixed rather than documented. Until it ships, avoid both shapes.
  • admin_contact_details.phone accepts less than the Phone schema it points at. A separator directly after the country code is rejected there and accepted on primary_phone and support_phone — so +1-800-555-1234, which this document publishes as an example on that schema, returns 400 on the admin contact. Also a service defect being fixed rather than documented. Until it ships, send that field without a separator after the country code: +18005551234.
  • website on POST /v1/clients is capped at 30 characters and this document still does not say so. A correction is planned. The cap comes from the acceptor URL field the processor carries.

1.0.0-beta.24 — 2026-09-04

Four properties that report a returned VeriCheck ACH transaction. The service already emits them — they shipped on 2026-09-04 — so this version is the document catching up, not a new capability.

That matters if you validate responses. TransactionResponse sets additionalProperties: false, so a client checking a live response against beta.23 has been rejecting every transaction since the service shipped. This version is the fix for that.

Added

  • TransactionResponse gains is_returned, returned_at, return_code and return_reason. An ACH transaction can be returned days after it was approved, and it can be returned after it has already settled. Nothing in the contract said so before this version.
  • One schema change reaches all seven operations that return a transaction, because TransactionsListResponse.transactions items point at this schema: POST /v1/transactions, GET /v1/transactions, GET /v1/transactions/{id}, POST /v1/transactions/{id}/capture, POST /v1/transactions/{id}/cancel, GET /v1/batches/transactions and GET /v1/batches/{id}/transactions.

What you have to do. Nothing, to keep compiling — all four are optional, the way every other property on this schema is. Two things are worth changing anyway:

  • Read is_returned alongside is_settled, not instead of it. A transaction can be both settled and returned. A returned transaction that never settled is also possible.
  • Anything computing a success state from result alone should now also check is_returned. An approved transaction that was later returned is not a successful one, and before this version you had no way to tell.

Known limitation

  • is_returned is false on a card transaction, never absent. Only ACH can be returned. The property is a plain boolean rather than nullable, so a card transaction reports false. Confirmed against dev on 2026-09-06 across 25 card rows and 39 ACH rows — all 64 carried the property.
  • return_code and return_reason are omitted, not null, when there is no return. Neither key appeared on any of those 64 rows. returned_at behaves differently: it is always present and null. So read all three with a key-missing check, not only a null check.
  • A returned transaction has not been observed on dev. No row in either sample carried is_returned true, so the shape of a populated returned_at is declared here rather than measured. It is typed format: date-time, and VeriCheck's own events feed reports timestamps as 2021-07-26 16:44:25 ET, which the gateway converts before returning it. If you see the ET form reach you, that conversion is missing and the value is violating this document — report it.
  • return_code is a free-form string, not an enum. The NACHA codes are not published as an enum with x-enumDescriptions yet. That work is planned. Do not switch on the value without a default branch.
  • No filter or sort by return state. GET /v1/transactions gains no query parameter here, so finding returned transactions means reading the flag on each row. Whether a return should also raise a webhook is an open question.
  • The emitted OpenAPI document names is_returned in its required list. That is an artifact of how the document is generated rather than a statement about the contract, and it is deliberately not carried into this document. Treat all four as optional, the way every other property on this schema is.

1.0.0-beta.23 — 2026-09-02

Required fields the gateway has always enforced and the spec never named. The API did not change. Every request this version rejects was already being rejected; the document simply now says so, and your generated SDK will now say so at compile time instead of leaving you to find out at runtime.

Breaking

  • POST /v1/integrations and POST /v1/integrations/{id} require 23 more fields on a TSYS integration_details. The list goes from six to twenty-nine: address1, agent_bank_number, agent_chain_number, amex_number, amex_opt_blue, association, bin, descriptor_city, descriptor_postal, descriptor_state, discover_number, industry, merchant_aba_number, merchant_settlement_agent_number, mid, mvv, reimbursement_attribute, sharing_group, store_number, terminal_id, terminal_number, url and v_number join the six already there. The gateway already rejected an empty value for every one of them, so a boarding request that omitted any of them was already returning 400.
  • The same 23 stopped accepting null. They were typed [string, "null"] and are now string — amex_opt_blue likewise becomes a plain boolean. .NotEmpty() rejects null as well as "", so the nullable typing was never true.
  • POST /v1/batches/close requires client_id, processor, batch_type and created_at. The gateway already rejected a request missing any of them. client_id and created_at also stop accepting null. batch_id is unchanged — omitting it still closes every open batch for the client, which is the normal call.

What you have to do. Regenerate, then expect compile errors wherever you build a TSYS integration or a batch close. Fill in the fields you were omitting. If your code already sent all of them, nothing changes for you but the type signatures.

Corrected

  • Fourteen integration_details fields now carry the maxLength the service enforces: address1 32, bank_number 4, descriptor_city 32, descriptor_country 3, descriptor_postal 10, descriptor_state 2, descriptor_store_number 4, fcsid 7, merchant_aba_number 9, merchant_settlement_agent_number 4, name 25, sharing_group 30, terminal_id 8, v_number 8. These mirror the TSYS G3v088 field widths and have always been enforced. A 40-character descriptor_city was returning 400 against a document that declared no limit.
  • TsysIntegrationRequestExample gained the four required fields it never carried — amex_number, association, bin, discover_number — and its bank_number dropped from 123456 to 1234, because six characters exceeded the cap the service applies. Both the request and response examples changed, so they still agree.

Known limitation

  • The spec still does not declare minLength: 1 on the 52 fields where the service rejects an empty string. Almost all of them are required fields whose .NotEmpty() rule already says so, and restating it as a length would state the same fact twice. The three that are not required — bank_number, mcc and name — are required too. So no field on the integration schema earned a length floor.

1.0.0-beta.22 — 2026-08-30

Added

  • card_type on GET /v1/transactions accepts UNKNOWN. The processor reports it when it cannot determine the card type, and it is the common case, so a filter of CREDIT or DEBIT alone returns very few rows.

Corrected

  • EntryMethod said "Currently the only option is ECOMMERCE" while its enum had carried MOTO since beta.20. The description now names both values. Document only — no request or response shape moved.

1.0.0-beta.21 — 2026-08-30

Breaking

  • The type filter on GET /v1/transactions no longer accepts VOID. Sending it returns a server error. A canceled payment is reported as CANCEL, so filter on that instead.

Added

  • The type filter accepts PAYOUT, REFUND, and CANCEL, and is now declared as an array so a comma-separated list is explicit — type=SALE,AUTH. Matching is case-insensitive.
  • phone on admin_contact_details is optional and accepts null. It was required.

Known limitation

  • Filtering by PAYOUT is accepted and then ignored. It returns unfiltered results rather than an error.

1.0.0-beta.20 — 2026-08-29

The reconciliation release. The spec was compared against the service's emitted Swagger, its C# source, and its validators, and corrected to describe what the API actually does. Twenty commits landed under this one version. If you are on beta.19 or earlier, regenerate your SDK and read this entry in full.

Breaking

  • entry_method moved from the transaction request root into payment_method. Send it inside the card object.
  • POST /v1/transactions/{id}/capture no longer takes type in the body. The endpoint implies it.
  • POST /v1/integrations no longer takes message, and no longer requires it.

Added

  • POST /v1/clients returns api_key. This is the only call that ever returns it. Store it as a secret; no later call reissues it.
  • metadata on the client, integration, and TSYS host detail objects.
  • correlation_id on the batch, client, and integration responses.
  • recurring on the transaction request and response, and initial_network_transaction_id on recurring_details.
  • settlement_issue on the transaction response.
  • currency on the transaction request.
  • entry_method on the card payment method, with MOTO alongside ECOMMERCE. Both are Card Not Present.
  • A declared 502 response, so a processor failure is part of the contract.
  • Vericheck onboarding request and response schemas.
  • The integration summary response, and VERICHECK on the integration filter.
  • Roughly forty TSYS host detail fields on the integration request and response.

Corrected

  • Processor takes Tsys and Vericheck, in that casing. The enum previously said TSYS and VERICHECK, which no integration has ever returned. This field is parsed case-sensitively, so the old spelling was always rejected.
  • phone became phone_number on the client and TSYS host detail objects, and fax became fax_number. The service has always used the longer names.
  • configuration_id became id on the integration responses.
  • Several fields the API returns as null were declared non-nullable, so a strongly typed SDK threw on real responses: website on the create-client response, initiator and recurring_details on the transaction response, and closed_at_date_time and settled_at_date_time on a batch.
  • BankAccountOutput requires type, so the payment method discriminator compiles.
  • PaymentMethodType split into PaymentMethodTypeInput and PaymentMethodTypeOutput, and transaction type split into TransactionCreateType and TransactionResponseType. A request and a response do not use the same vocabulary, and one enum could not describe both.
  • Several integer fields were declared as number, which generates a floating point type in most SDKs. They are now integer.

1.0.0-beta.19 — 2026-08-24

Corrected

  • Version bump only. No content changed, and beta.18 was never published.