Updates to the Aeropay API, including new features, changes, and fixes.
Overview
The Aeropay Developer Portal receives regular updates, incorporating new API features, additional documentation, and resolutions for known issues. The release notes provided here detail these updates, with fresh notes accompanying each release of an updated API or new API. All Aeropay API versions will be backwards compatible unless specifically stated otherwise.
Release notes
Each API and SDK product within the Aeropay ecosystem will have updates listed in the Aeropay release notes:
- Aeropay API
- Aeropay SDK
Aerosync API release notes can be found in the Aerosync Developer Portal.
2026-10-20 — Clearer error for unsupported characters in preauthTransactions queries
preauthTransactions queriesGET /v2/preauthTransactionsQuery parameters containing a single quote, double quote, backslash or control character previously returned HTTP 500 withAP-0002. They now return HTTP 400 with{"error": {"code": "AP701", "message": "Improperly Formatted Parameter"}}. Requests that already worked are unchanged. v2 only.
2026-10-15 — Queued refund webhooks and refund status fixes
Refunds that were queued behind a pending transaction now notify and report more accurately.
transaction_refundedwebhook When several queued refunds on one transaction are released together, one webhook is now sent per released refund. Before, v2 sent one webhook listing every refund, and v1 sent one webhook with only the last refund.- v2 payload:
refundTransactioncontains only that refund, andqueuedRefundslists the refunds still queued right after it was created. - v1 payload: each webhook carries its own refund.
- Webhooks from the same release may arrive out of order, so integrators should not rely on delivery order.
- v2 payload:
POST /transactionSearchandPOST /v2/transactionSearchA declined (nsf/declined) transaction that has not been resolved now reports its declined status even when refund activity exists. Before, it reported a refunded or refund-queued status. A resolved decline (bounced and then successfully retried) and non-declined refunds are unchanged. v1 and v2 share the same search query, so both are affected.- On v1,
refundAmountis only returned when the status is refunded or refund-queued, so a declined transaction with refund activity no longer includesrefundAmount. v2 search results do not includerefundAmount. - The merchant transaction CSV export status column is changed in the same way.
- On v1,
2026-09-29 — v2 credentials error, refund retry, aggregator linking and webhook typing
POST /v2/tokenInvalid API key or secret key now returnsAP001with the messageinvalid API key or secret key. Before, it returnedAP002. This applies to merchant, user and userForMerchant scopes. v2 only: v1POST /tokenstill returnsAP002. The user/merchant-associationAP002("User ID doesn't exist or hasn't been associated with this merchant.") is unchanged on both versions. v2 clients that matchedAP002for bad credentials must now matchAP001.POST /v2/linkAccountFromAggregatorv2 no longer returnsAP300(a transaction-level error) from this bank-linking endpoint. v1 is unchanged.- If the account links but its balance cannot be retrieved, the request now succeeds with HTTP 200 and returns
userBankInfofor the linked account, instead of failing. - If the aggregator returns no account details at all, nothing is saved and v2 returns
AP410with HTTP 200 and a message like "We were unable to retrieve the bank account information for this connection (reference:<code>). Please reconnect your bank account and try again." (the reference part appears only when the aggregator supplies an error code). - A
balanceUnavailablevalue sent in the request body is ignored.
- If the account links but its balance cannot be retrieved, the request now succeeds with HTTP 200 and returns
POST /reverseTransaction/POST /v2/reverseTransactionA refund can now be retried when the earlier refund attempt failed (its reversal transaction endednsforvoid). Before, the original transaction still counted as fully refunded and the retry returnedAP1300("Transaction has already been refunded"). Failed attempts no longer count toward the refunded amount. A refund against a transaction that is itself a reversal is now allowed only when every prior refund attempt against it failed. Otherwise it is still rejected (AP1304, unchanged).AP1300is still returned when a refund actually processed.user_suspended/user_activewebhooksdata.useridis now always sent as a JSON string. It was sometimes sent as a number when the upstream user id was an integer, which broke strictly typed consumers. Applies to both v1 and v2 payload versions. Only future events are affected.
2026-09-22 — Location id validation, bank-link duplicate message and blocked-user 403
merchantLocationId(v2 requests) The documented "location UUID" form is now accepted. Before, validation required a standard RFC 4122 UUID, so a real location UUID (a 10-character lowercase hex value) was always rejected withAP701"invalid UUID format". A standard 36-character UUID is now rejected. The integer location id form is unchanged. This also applies toPOST /v2/paymentLink.- Bank account linking
AP410now uses a specific default message: "This account is already connected to another user. Please share the last four digits with [email protected] to investigate." It is used when a bank account is already linked to another user, replacing the previous message "Bank account already exists". The defaultAP410message used by other link failures changed to the same text. If you match onAP410message text, switch to matching the code. GET /userThe blocked-user 403 option (AP120with HTTP 403), available on v2 since 2026-08-18, now also applies to v1GET /userfor merchants where it is enabled.transaction_declined(RTP declines) andtransaction_rfp_deliveredv2 webhooksdata.userIdnow contains the user'saeroPassUserUuidinstead of the raw numeric id. If no matching uuid exists, it falls back to the numericuserIdas before.
2026-09-17 — AP307 reason codes on model-rejected transactions
POST /transaction/POST /v2/transactionAnAP307(model reject) error can now include areasonCodesarray when reason codes are available. On v2 the field iserror.reasonCodes. On v1 it is a top-levelreasonCodesnext tosuccess,codeanderror. This includes v1 requests sent withX-AP-Version: 2023-06-05. The field is omitted when no reason codes are available. Other transaction endpoints do not return it.
2026-09-03 — v1 transaction_refunded webhook for queued refunds
transaction_refunded webhook for queued refundstransaction_refunded(v1 payload) Queued refunds previously leaked v2-shaped fields. The payload now matches the flat legacy contract:statusis"queued",idisnull(no refund transaction exists yet),amountis a dollar string andapFeeis"0.00". A newreversalIdfield (the refund record id) is added to the payload for a refund that is queued and to the payload for a refund that is processed immediately. A queued refund that is released later does not includereversalId. The v2 payload is unchanged and does not includereversalId.
2026-09-01 — v2 paymentLink contract, serialization fixes and transaction_resolved for resolved NSF
paymentLink contract, serialization fixes and transaction_resolved for resolved NSFPOST /v2/paymentLinkv2 now has its own validated request and response contract. v1POST /paymentLinkis unchanged. v2 callers that relied on the old unvalidated behavior must update.uuid,transaction_uuid,locationIdandsubscriptionIdare rejected withAP701("Field(s) not supported: ...").paymentTypeofsubscriptionis rejected withAP701.merchantLocationIdreplacesuuidandlocationId. It accepts an integer location id (a numeric string also works) or a location UUID. A location UUID that belongs to a different merchant returnsAP204. An integer location id that does not belong to the merchant returnsAP203("Unable to get location for merchant id."). A location UUID that does not exist returnsAP208. (The location-UUID format check was corrected on 2026-09-22.)amountaccepts the v2 money shape{"amount": ..., "currency": ...}.referenceIdis accepted and validated, echoed in the response, and added to the generated link as anrquery parameter.- Missing both
phoneandemailnow returnsAP700immediately. Before, it only failed silently after the request was queued.
- v2
userIdvalues Some older migrated accounts whose stored username was a prefixed string now return the bare UUID inuserId. This coversGET /v2/userand all v2 transaction objects (reverseTransaction,GET /v2/transaction,transactionSearchand v2 webhooks). - v2 missing values v2 bank account objects now return JSON
null, instead of the literal strings"None"or"undefined", for a missingbankNameorstatus. v2 transaction objects and v2 webhooks returnnullfor a missing attributevalue,nameordescription. v1 responses and v1.1 webhook payloads keep the legacy string values. transaction_resolvedwebhook: it is now also delivered when a declined (nsf) transaction is marked resolved by Aeropay support. Before, it was delivered only when a retried return was processed. The payload is the same as for the existing topic: the original (declined) transaction, in the standard v1 or v2 shape for the subscriber, withuserIdas the user UUID when available. Marking a transaction as no longer resolved does not send the webhook.
This applies to v1 and v2 webhook subscriptions. If you subscribe to transaction_resolved, expect events that no retried return preceded, and make your handler idempotent per transaction. No other action is required.
2026-08-27 — Merchant tip configuration endpoint
Merchants can now read their tipping configuration through the v2 API.
GET /v2/merchant/tipConfigurationis a new endpoint. It requires a merchant-scoped bearer token and returns the authenticated merchant's tip settings. Tipping is configured per merchant, not per location.- When tipping is enabled, the response is
{"tipConfiguration": {"enabled": true, "options": [{"type", "value"}], "defaultValue": null, "customTipEnabled": <bool>}}. typeispercentageorflat. Forpercentage,valueis a number. Forflat,valueis a money object{"amount": <integer pennies>, "currency": "USD"}.customTipEnabledistruewhen the merchant offers a custom-tip option. The custom option is not repeated inoptions.defaultValueis always present but is currentlynull. No default tip selection is stored yet.- When tipping is disabled, the response is
{"tipConfiguration": {"enabled": false}}, and the other keys are omitted. - A token without merchant scope returns
AP101with HTTP 401. Unhandled failures returnAP-0002with HTTP 500.
- When tipping is enabled, the response is
This is a v2-only endpoint. No integrator action is required.
2026-08-18 — v2 apFee standardization
apFee standardizationAeropay API v2 monetary representation is now consistent between amount and apFee:
POST /v2/transaction,GET /v2/transaction, andPOST /v2/reverseTransactionno longer return anapFeefield. Fee amounts are no longer surfaced on these endpoints.POST /v2/transactionSearchnow returnsapFeeas an object matching the existingamountshape —{ "amount": <integer, cents>, "currency": <string> }— instead of a dollar-denominated string (e.g."0.25").
This is a v2-only change; v1 endpoints, including CSV and totals reports, are unaffected. No action is required for integrations that don't read apFee from these endpoints today.
This change is controlled by a feature flag. The new behavior applies by default, but the previous apFee behavior can be temporarily retained for individual merchants during migration. If your integration reads apFee and needs more time to update, contact Aeropay support.
2026-08-18 — DELETE webhook response, blocked-user 403, and payout location validation
DELETE /v2/webhook: a successful delete now returns{"webhooks": [ ... ]}, listing every webhook that was deleted. Previously the response body was an empty object{}.DELETE /v2/webhook: deleting a webhook that does not exist (no match fortopic/webhookId) now returns HTTP 404 with{"error": {"code": "AP1109", "message": "No webhook found for topic=... webhookId=..."}}. Previously this case returned HTTP 200 with an empty object.DELETE /webhook(v1): the same no-match case now returns HTTP 200 with{"error": "<message>", "success": false, "code": "AP1109"}.GET /v2/user: for a blocked (suspended) user, the call returnsAP120with HTTP 403. The message is "Your Aeropay account requires review before additional transactions can be processed. Please contact [email protected] for assistance." This applies for merchants where this is enabled. Otherwise behavior is unchanged.POST /v2/payoutTransaction:merchantLocationIdis now validated like it is forPOST /v2/transaction. Previously payouts skipped the check. A location that does not exist, or that belongs to another merchant, returnsAP303with HTTP 400. IfmerchantLocationIdis omitted, the merchant's online location is used, as before; if the merchant has none,AP203is returned. This also applies to v1POST /payoutTransaction, which returnsAP303with HTTP 200 and the error in the body.POST /v2/transaction:AP303now returns HTTP 400. TheAP303message changed from "Transaction missing location." to "Transaction location is missing or invalid." The new message also applies to v1.POST /v2/paymentLink: errors now return HTTP status codes instead of 200.AP009andAP101return 401.AP700andAP701return 400. All other errors still return 200.
The DELETE /v2/webhook, GET /v2/user, POST /v2/transaction (HTTP 400 for AP303) and POST /v2/paymentLink changes are v2-only. The v1 POST /payoutTransaction and POST /transaction endpoints use the same location validation and the new AP303 message, and still return HTTP 200 with the error in the body. Integrators that call DELETE /v2/webhook should read the webhooks array and handle the 404 AP1109 response. Payout callers must either omit merchantLocationId or send one that belongs to their merchant.
2026-08-13 — Webhook payload field
- v2 transaction webhooks: payloads now include
merchantUserReputation, with the valuestandard,vip, orblocked. It appears in thedataoftransaction_completed,transaction_declined,transaction_resolved,transaction_voided,transaction_rfp_delivered,transaction_refunded(ondata.transaction), andpreauthorized_transaction_created. The field is omitted when the reputation status cannot be resolved. v1 payloads never include it.
This is a v2-only change. The new webhook field is additive. No integrator action is required.
2026-08-11 — HTTP 409 for idempotency errors on reverseTransaction
POST /v2/reverseTransaction:AP1400(idempotency key reused with a different body) andAP1401(same key still being processed) now return HTTP 409. All other error codes on this route still return 200.GETon this route is unchanged.
This is a v2-only change. Integrators that checked only for HTTP 200 with an error body should handle 409.
2026-08-07 — Idempotency error code and merchant-not-enabled message
POST /v2/reverseTransaction: reusing anIdempotency-Keywith a different request body now returns error codeAP1400. The message is "This operation is idempotent and it requires correct usage of Idempotency Key. Idempotency Key cannot be reused across different payloads of this operation". It previously surfaced as a generic error.AP210message (returned when the merchant is not verified) changed from "This merchant is not activated, please contact [email protected] for more information." to "This merchant is not currently enabled for Aeropay transactions. Please have them contact [email protected] for assistance."
Integrators that match on message text for AP210 should match on the code instead.
2026-07-30 — Duplicate webhook subscriptions rejected
POST /v2/webhook(andPOST /webhook): creating a subscription with the same merchant,topic,url, and payload version as an existing one now returnsAP1108with the messageDuplicate webhook subscription for topic {topic} and url {url}. Previously duplicates were accepted.
Integrators should treat AP1108 as "already subscribed". No action is needed for existing subscriptions.
2026-07-28 — AP118 passthrough and AeroSync account display
POST /v2/user(andPOST /user):AP118("User cannot be created with numbers or special characters") is now returned to the caller. Previously it was caught and replaced by a generic error.- User bank account objects (v1 and v2): for accounts linked through AeroSync,
accountLast4returns the AeroSync-provided account number display value when one is stored, instead of the last 4 digits of the account number. The display value is saved when the account is linked, or when a balance check finds none stored. Other aggregators are unchanged.
Integrators that assume accountLast4 is exactly 4 characters should not rely on that for AeroSync accounts.
2026-07-23 — New transaction_resolved webhook, merchantReputation timeout, AP317 message
- New webhook topic
transaction_resolved: it is delivered when a retried return is processed and the original declined transaction is marked resolved. The payload is the original (declined) transaction, not the retry. v2 subscribers receive the standard v2 transaction object indata, withuserIdas the user UUID when available. The topic is accepted byPOST /v2/webhookandPOST /webhook. POST /v2/merchantReputation(andPOST /merchantReputation): fixed 504 timeouts for merchants with very large user lists. The check that the submitteduserIds belong to the merchant now looks up only the submitted users. There is no change to request or response shape.AP317message changed from "This user or transaction has been throttled for security and AML risks" to "Too many failed attempts. Please wait a few minutes and try again. If the problem continues, please contact support."
Subscribe to transaction_resolved to learn when a previously declined transaction has been cleared. No action is required for existing integrations.
2026-07-21 — Error messages, refund webhook payloads, and token error
AP314message changed from "Transaction already exists" / "Transaction with uuid of{uuid}already exists" to "This transaction request has already been submitted. Please refresh the page or session before trying again." The message no longer includes the uuid.transaction_refundedwebhook (v2): fixes to refund payloads.data.transaction.userIdis the user UUID.data.refundTransactionanddata.queuedRefundsare now always taken from the full refund lists. When a transaction is voided because of an NSF return, its queued refunds are reported withstatus: "abandoned", and any processed reversals are included inrefundTransaction. Abandoned and queued refund items inqueuedRefundsno longer includepaymentType; each carriesid,referenceId,amountandstatus.POST /v2/tokenandPOST /token(user scope): when theuserIddoes not exist or is not associated with the merchant, theAP002message is now "User ID doesn't exist or hasn't been associated with this merchant." It was "invalid API key or secret key". The code is stillAP002.
Integrators that match on message text should match on error codes instead.
2026-07-16 — Pagination fix
POST /v2/transactionSearch(andPOST /transactionSearch):paging.nextPageis nowfalsewhen the returned page has no items. Previously it could reporttruefor an empty page.
No action is required.
2026-07-14 — Reversal uuid validation
POST /v2/reverseTransaction: the optionaluuidfield is no longer required to be a version 4 UUID. Previously a non-v4 value returnedAP701foruuid. Auuidthat already exists on a reversal still returnsAP314.
No integrator action is required.
2026-07-09 — transaction_refunded webhook payload
transaction_refundedwebhook (v2):data.refundTransactionentries are now full v2 transaction objects for processed reversals (id,amount,status,paymentType,userIdas UUID,referenceId, and so on). Previously they carried onlyid,referenceId,amount,status: "pending", andpaymentType: "reversal".data.queuedRefundskeepsid,referenceId,amount, andstatus: "queued".referenceIdis reported at the top level of each object.
This is a v2 webhook payload change. Integrators should expect the larger object shape in refundTransaction.
2026-07-07 — Webhook payload fixes and bank account limit enforcement
user_suspendedanduser_activewebhooks (v2):data.useridis now the user UUID (aeroPassUserUuid) when one exists, falling back to the integer id. v1 payloads are unchanged.transaction_declinedwebhook (v2): for declines caused by ACH returns,referenceIdis now reported at the top level ofdata, not insidedata.attributes.- Bank account linking: the configured per-user limits are now enforced.
AP414(unique-account limit) counts a user's active linked accounts, so removing an account frees a slot.AP415(per-account re-link limit) counts every link of the same routing and account number, including removed ones. Previously both limits could be skipped, including when the two configured limit values were equal. Users at or over the limits now receiveAP414orAP415.
Integrators using the v2 webhook payloads should read userid as a UUID where available.
2026-06-25 — v2 transaction webhook field consistency
The v2 payloads for transaction_completed, transaction_voided, transaction_declined and transaction_refunded now return three fields in the same format across these topics.
userIdis now the consumer UUID (theaeroPassUserUuidvalue) instead of the numeric user id, on all four topics. If the UUID cannot be resolved, the numeric id is still sent.referenceIdis now a top-level field on the transaction object. When a transaction carried it only as an entry inattributes(name: "referenceId"), the value is promoted to the top level and that entry is removed fromattributes.userAccountIdis now present (as an integer) ontransaction_completed. It was previously dropped before the webhook was sent.
This affects v2 webhook payloads only. Integrators that matched on a numeric userId in v2 webhooks must expect UUIDs.
2026-06-18 — Rejected request-for-payment (RFP) events now void the transaction
transaction_voidedwebhook (RFP): a payer rejection reported by the RTP provider (Rtp.Payment.Rejected) is now handled the same as a cancellation (Rtp.Payment.Canceled), including when the request had already been delivered to the payer. The RFP is markedrejected, the transaction is voided, and thetransaction_voidedwebhook is sent.
Applies to merchants using RTP request-for-payment.
2026-06-16 — v2 transaction_refunded webhook payload reshaped
transaction_refunded webhook payload reshapedtransaction_refundedwebhook, v2 payload (breaking):data.refundTransactionchanges from a single object to an array of refund objects, and a newdata.queuedRefundsarray is added. The shape is now{ "transaction": {...}, "refundTransaction": [ ... ], "queuedRefunds": [ ... ] }. It has the same contents as theGET /v2/transaction/{id}/refundsresponse, except that the refunds list is under the keyrefundsthere andrefundTransactionhere.- When a refund is queued,
refundTransactionis[]andqueuedRefundslists every queued refund on the transaction (status: "queued"). - When a queued refund is processed,
refundTransactionlists the refund(s) andqueuedRefundsholds anything still queued. - When queued refunds are abandoned because the parent transaction went NSF or void, one webhook is sent per transaction. It carries all abandoned refunds in
queuedRefundswithstatus: "abandoned".
- When a refund is queued,
This is a v2-only change. v1 payloads keep the single flat refund object. Integrators on v2 must update their parsing.
2026-06-11 — RFP void reason attribute
- RFP voids: when a request for payment is voided because the provider reported a rejection reason, a
voidReasonattribute is saved on the transaction and returned in itsattributesarray (name: "voidReason",valueis the reason code,descriptionis the end-user message).
Applies to RTP request-for-payment voids that carry a rejection reason.
2026-06-09 — Refund identifiers (uuid, referenceId) and queued refund detail
uuid, referenceId) and queued refund detailPOST /v2/reverseTransaction: the request body accepts two optional fields.referenceIdis stored as areferenceIdattribute on the refund.uuidsets the id of the refund (queued or immediate); if omitted, one is generated.uuidmust be a valid UUID v4, otherwise the request fails withAP701(parameteruuid). Auuidthat is already used by another refund fails withAP314.
POST /v2/reverseTransaction(queued refunds): when the refund is queued (action: "queued"),refundTransactionis now an object describing the queued refund, withid(the refund uuid),referenceIdwhen provided,amount({ "amount", "currency" }) andstatus: "queued".GET /v2/transaction/{id}/refunds(breaking): the response fieldqueuedRefund(a single object withamountandcurrency, summing all queued refunds) is replaced byqueuedRefunds, an array. Each item hasid,referenceId,amount({ "amount", "currency" }) andstatus: "queued". When nothing is queued the value is[](previouslynull).- Queuing behavior: each queued refund request is now stored separately with its own
uuid. Previously a second queued refund on the same transaction was merged into the existing queued amount. transaction_refundedwebhook: a webhook is now also sent when a refund is queued (in addition to when it is created). The webhook payload shape changed again on 2026-06-16, see above.
v2 only. Integrators reading queuedRefund from GET /v2/transaction/{id}/refunds must switch to queuedRefunds.
2026-05-21 — Abandoned queued refunds now notify, plus preauth not-found message
transaction_refundedwebhook: when a queued refund is discarded because its parent transaction ended as NSF or void, atransaction_refundedwebhook is now sent with the refundstatus: "abandoned"andpaymentType: "reversal". Previously the queued refund was deleted with no notification. The v2 payload shape for this event changed on 2026-06-16, see above.GET /v2/preauthTransaction/{id}(not found): looking up a preauthorized transaction that does not exist still raisesAP313, but the message changes from "Preauth ineligible for capture" to "Preauthorized Transaction with status of not_found not eligible for capture". The same message applies to the v1 lookup by id.
2026-05-07 — v2 webhook payload fixes
- v2 webhooks (
transaction_completed,transaction_declined,transaction_voided,transaction_rfp_delivered,transaction_refunded): when the pre-built v2 transaction object is missing from the event, the v2 payload is now generated from the raw transaction. It has nestedamount({ "amount", "currency" }), ISO 8601createdDate/modifiedDate,referenceIdandisRtp. Previously the v1-style shape was sent to v2 subscribers in these cases. transaction_voidedwebhook:statusis now alwaysvoidin both the v1 and v2 payloads (v2:data.status).
The first applies to v2 subscribers only; the second applies to all subscribers.
2026-05-05 — v1 webhook payloads: field filtering restored, wider preauthorized field list
- v1 transaction webhooks: payloads for
transaction_completed,transaction_declined,transaction_voided,transaction_refundedandtransaction_rfp_deliveredare limited to the documented set of fields. A release on 2026-04-30 changed the fields sent in v1 payloads; this restores the documented field list, which now also includesreturnCode. preauthorized_transaction_createdwebhook (v1): the payload now includes these additional fields:transactionId,merchantAccountId,merchantLocationId,userAccountId,userName,userEmail,dollarAmount,originalAmount,description,merchantReputationStatus,userReputationStatus,expiryDate,cancellationDate,creditPartnerId.
v1 webhooks only. v2 payloads are unchanged.
2026-04-23 — Idempotency key lookup: encoding and whitespace
GET /v2/transaction/idempotency/{idempotencyKey}: the path value is now URL-decoded, so keys containing#can be sent as%23.GET /v2/transaction/idempotency/{idempotencyKey}: the key is trimmed of leading and trailing whitespace before lookup.
v2 only.
2026-04-21 — AP115 and AP119 show the upstream message
POST /v2/user(and v1 user creation): when the phone number (AP115) or country code (AP119) is rejected and a message is supplied, that message is returned in the error. Otherwise the previous fixed text is returned. The error codes are unchanged.
Applies to v1 and v2.
2026-04-14 — New endpoint: look up a transaction by idempotency key
GET /v2/transaction/idempotency/{idempotencyKey}: returns the response originally stored for aPOST /v2/transactioncall made with thatIdempotency-Keyheader value. Requires a merchant-scoped token.- Returns an empty result (no transaction data) if the key does not exist or belongs to another merchant; the two cases are not distinguishable.
- Keys are retained for one day, after which the transaction cannot be found by key.
- An empty key fails with
AP700(HTTP 400).
v2 only. Follow-up changes shipped on 2026-04-23, see above.
2026-04-02 — Webhook payloadVersion format
payloadVersion format- Webhooks (v2):
payloadVersionis now always a decimal string, for example"2.0"instead of"2". Values that already contain a decimal (for example"1.1") are unchanged. This applies to thepayloadVersionfield on webhooks delivered to merchants (includingpreauthorized_transaction_created) and to the webhook object returned for v2 callers.
Integrators that compared payloadVersion == "2" must update to "2.0".
2026-03-31 — Clearer errors from POST /v2/reverseTransaction and GET /v2/transaction/{id}
POST /v2/reverseTransaction and GET /v2/transaction/{id}Failures that used to come back with a generic error code now return specific codes.
- Transaction not found returns error code
AP316with the messageTransaction {id} not found, where{id}is the transaction UUID on v2. The message text is unchanged; theerror.codewas previously the genericApError. This applies toPOST /v2/reverseTransactionandGET /v2/transaction/{id}. - Refunding a transaction in a status that cannot be refunded (for example void or NSF) returns error code
AP1306. The message is nowCannot reverse transaction with status of {status}. It was previouslyCan not reverse with status of {status}under the genericApErrorcode. - Missing-parameter errors (
AP700) now name the v2 parameter.GET /v2/transaction/{id}reportsidinstead oftransactionId.POST /v2/reverseTransactionalso reportsid. - Refund success message (v2):
messagenow cites transaction UUIDs, asRefund transaction {refundUuid} created for transaction {originalUuid}. It previously cited numeric ids. This applies to v2 refunds that are processed immediately. - Unauthorized requests to the v2 pay API (HTTP 401 from the gateway authorizer) now return a fixed body:
{"error": {"code": "AP008", "message": "Invalid token."}}.
The AP316 and AP1306 codes are not version-gated in the code. The AP700 parameter name and the refund success message change apply to v2 only. Integrators that match on the generic ApError code or on the old message text for these cases should switch to AP316 and AP1306.
2026-03-26 — Request-for-payment expiry shortened to 15 minutes
- Request-for-payment (RFP) expiry: RFPs created for instant pay-in now expire 15 minutes after creation. Before this change the expiry was set to the date after creation (
YYYY-MM-DD, no time). An expired RFP voids the transaction (see the 2026-03-24 entry).
Applies only to merchants and transactions that take the RFP path.
2026-03-24 — Instant pay-in (request for payment)
POST /transactionandPOST /v2/transactioncan now pay in instantly through a request for payment (RFP) sent to the user's bank. This applies to payment transactions that setrtptotrue(onPOST /transactionit applies to requests that sendX-AP-Version: 2023-06-05). The transaction is eligible only if the request'suserIdentificationincludes a full address or a full birth date, the merchant is set up for instant pay-in, and the user's bank supports request for payment. If it is not eligible, the transaction is processed as ACH as before. An RFP transaction stayspendinguntil the user approves the request in their banking app. When the user approves, it becomes completed and thetransaction_completedwebhook is sent. If the user rejects it, or it expires or fails, the transaction is voided andtransaction_voidedis sent.- New webhook topic
transaction_rfp_deliveredis sent when the request for payment reaches the user's bank. It can be subscribed to withPOST /webhookorPOST /v2/webhook. The payload is always the v2 transaction object.
Applies to merchants set up for instant pay-in.
2026-03-19 — Idempotency-Key format relaxed on POST /v2/reverseTransaction
Idempotency-Key format relaxed on POST /v2/reverseTransactionPOST /v2/reverseTransaction: theIdempotency-Keyheader no longer has to be a UUID v4. Any non-empty value without whitespace is accepted. A key that is empty or contains whitespace is still rejected.
This applies to v2 only. No action is required; keys you already send keep working.
2026-03-05 — connectionId on v2 bank account objects
connectionId on v2 bank account objects- v2 bank account objects (including each account returned by
GET /v2/bankAccounts) now includeconnectionId. It holds the AeroSync connection (user) id for AeroSync-linked accounts. It isnullfor other accounts, such as Plaid, or when there is no stored link token.
This applies to v2 only. The change is additive.
2026-03-03 — Safe retry on POST /v2/reverseTransaction
POST /v2/reverseTransactionPOST /v2/reverseTransactionsupports safe retries through the optionalIdempotency-Keyrequest header. The header value must be a UUID v4. A retry with the same key and merchant returns the stored response instead of refunding again. The same key with a different body is rejected, and the error message contains "This operation is idempotent and it requires correct usage of Idempotency Key. Idempotency Key cannot be reused across different payloads of this operation", prefixed with "Error getting or creating the Idempotency: ". The same key used by a different merchant is also rejected. A request that arrives while the first is still in flight returns a conflict with the messageAnother refund in progressand error code409. Stored records are kept without expiry. Requests without the header behave as before.
This is v2 behavior. To make a refund request safe to retry, send a unique UUID v4 Idempotency-Key and reuse it with an identical body on each retry.
2026-02-12 — Corrected POST /v2/confirmUser output
POST /v2/confirmUser outputPOST /v2/confirmUsernow returns the user in v2 form:userwithid(the user UUID),firstName,lastName,type,email,phoneNumberandcreatedDateas an ISO 8601 UTC timestamp. Before, theidvalue in the v2 output was not the UUID, and the user lookup ignored the API version.
The confirmUser change is v2 only.
2026-01-29 — POST /v2/user existing-user response key
POST /v2/user existing-user response keyPOST /v2/userwhen the user already exists now returns the user under the keyuser(together withdisplayMessage). It previously used the keyexistingUser. This is v2 only. v1 still returnsexistingUser. Integrators readingexistingUserfrom v2 responses must switch touser.
2026-01-27 — v2 response cleanups (dates, voids, confirmUser)
- v2 transaction objects now return
createdDateandmodifiedDateas ISO 8601 UTC timestamps. Epoch-second strings are converted. This applies to the transaction objects built for v2 responses, includingrefundTransactionfromPOST /v2/reverseTransaction. POST /v2/reverseTransaction: when the reversal is a void of an unbatched pending transaction,refundTransactionin v2 now holds the voided transaction. Before it was built from the reversal record, which does not exist in the void case.POST /v2/confirmUser: the v2userobject is now built from the verified user.createdDateis formatted as an ISO 8601 UTC timestamp. The previous formatter silently dropped numeric-string dates and returnednull.
All items are v2 only.
2026-01-22 — User UUIDs and consistent v2 shapes
GET /v2/userandPOST /v2/usernow returnuserin the v2 shape:id(user UUID),firstName,lastName,type,email,phoneNumber,createdDate.GET /v2/useralso includesuserStatus(ActiveorSuspended). The phone field is namedphoneNumberrather thanphone.- v2 bank account objects (for example the response of
PATCH /v2/userBankAccount/{bankAccountId}) no longer includeconnectionTokenorexternalBankAccountId. POST /v2/capturePreauthTransactionnow returns the pre-authorization withstatusset tocompleted. The v2 preauth object is rebuilt after the transaction id is saved to the preauth. Before it was not refreshed.- Webhooks, v2:
transaction_refundednow delivers{"transaction": <original v2 transaction>, "refundTransaction": <v2 refund transaction>}instead of a single transaction object.preauthorized_transaction_createduses the v2 preauth object for v2 calls. Transaction objects in v2 webhook and reverse responses carry the user UUID inuserId. POST /v2/reverseTransaction: refunding a transaction whose refunds already total the full transaction amount returnsAP1300(transaction already refunded).
Items are v2 only unless noted. The AP1300 check is in the shared refund logic and is not version-gated, so it also applies to v1 refund requests.
2026-01-13 — v2 webhook, transaction search and preauth shapes
- Webhooks, v2: for events that originate from v2 API calls, the
transaction_completed,transaction_refunded,transaction_voidedandtransaction_declinedpayloads now use the v2 transaction object. Before, they were the truncated v1 shape. ThereturnCodeis still added fortransaction_declined. For v1 subscribers (events not originating from v2 calls), thetransaction_refunded,transaction_voidedandtransaction_declinedpayloads now also includedeliveryMethod, astransaction_completedalready did. POST /v2/transactionSearch: results are now built as v2 transaction objects, enriched with customer, location and merchant names, withuserIdset to the user UUID. The response containstransactionsandpaging.GET /v2/preauthTransactions:userIdon each returned preauthorization is now the user UUID instead of the numeric user id.GET /v2/preauthTransactions: the response now has the same v2 shape whether or not the single-preauth id (UUID) query parameter is sent. Before, the v2 formatting was skipped when it was sent.GET /v2/preauthTransactions: the date filtersdateStartanddateEndacceptMM-DD-YYYY HH:MM:SSorMM-DD-YYYY. A date-only end date is treated as 23:59:59. An unparseable date now returns the genericApErrorcode with the messageIncorrect date format, can be either %m-%d-%Y %H:%M:%S or %m-%d-%Y: {value}. Before, a bad date was silently replaced by 1970-01-01 (start) or now (end). Thetimezonequery parameter is no longer applied todateStartanddateEnd; the dates are used as sent.
The v2 webhook payload change and the userId change are v2 only. The date-filter change is not version-gated and also applies to v1 GET /preauthTransactions.
2026-01-08 — userId in reverse transaction response
userId in reverse transaction responsePOST /v2/reverseTransaction:refundTransactionin the v2 response now carries the user UUID inuserId, along withmerchantId,userAccountIdandamount. This is v2 only.

