Skip to content
API DocsDocs

Transaction Reports (S2S)

Programmatically export card, APM, payout and refund reports as CSV or XLS

8 min readUpdated Oct 12, 2026

Export transaction reports for a date range as CSV or XLS, backend-to-backend. This is the server-to-server equivalent of the Export Transactions feature in the Exirom Merchant Portal. Use it to automate reconciliation, accounting and other downstream integrations on a schedule instead of exporting by hand.

Reports are generated asynchronously:

  1. POST /api/v1/report-generator/s2s/async — submit a report request and receive its id.
  2. GET /api/v1/report-generator/s2s/async/{id} — poll until status is READY (or FAILED).
  3. GET /api/v1/report-generator/s2s/async/{id}/download-url — get a temporary download link for the file.

Base URL. Contact your Exirom account manager for the correct sandbox or production base URL. The examples use the sandbox host https://sandbox.api.exirom.com/api.

Deprecated: POST /api/v1/report-generator/s2s. The synchronous endpoint holds the connection open until the file is ready and then streams it. It keeps working and accepts the same request body, but it is deprecated and will be removed. Use the async endpoints for all new integrations — see Synchronous endpoint (deprecated).


#Authentication

Every report endpoint uses merchant key/secret headers — not the Bearer token used by the rest of the API. Keep the secret confidential; it is for backend use only.

HeaderRequiredDescription
x-merchant-keyYesYour public merchant API key
x-merchant-secretYesYour private merchant API secret
Content-TypeYes (POST)Must be application/json

A report is visible only to the merchant that created it. Polling or downloading another merchant's report ID returns 404.

#IP whitelisting

Report endpoints accept requests only from whitelisted IP addresses. Before your first call, send the public IP addresses of the servers that will request reports to your Exirom account manager, separately for sandbox and production. Requests from any other IP are rejected, even with valid credentials. Tell your account manager before your outbound IPs change.


#1. Submit a report

POST https://sandbox.api.exirom.com/api/v1/report-generator/s2s/async

#Request body

FieldTypeRequiredDescription
dateFromstringYesStart of range: YYYY-MM-DD HH:MM:SS, or YYYY-MM-DD for 00:00:00 of that day
dateTostringYesEnd of range, inclusive. Same format. YYYY-MM-DD means 00:00:00 of that day, so add 23:59:59 to include the whole day. The range can span at most 10 days. Any other format is rejected with 400
sourceTypestringNoWhich records to export (see Report types). Default CARD_PAYMENT. Any other value is rejected with 400
dateSourcestringNoWhich timestamp the range filters on: createdDate (default) or lastModifiedDate
timezonestringNoTimezone of dateFrom/dateTo and of the platform timestamps in the file (created and last modified date; orderDate is shown as sent). IANA name (UTC, Europe/London, Asia/Dubai) or offset (+05:00). Default: platform time, Europe/Nicosia. Case-sensitive: an unrecognised value (utc, PST) is rejected with 400
formatstringNoCSV (default) or XLS

#Report types

sourceTypeContents
CARD_PAYMENTCard payments (default)
CARD_PAYOUTCard payouts
CARD_REFUNDCard refunds
APM_PAYMENTAlternative payment method payments
APM_PAYOUTAlternative payment method payouts

Each report covers one type. To export several types, send a separate request for each.

#Date source

  • createdDate — rows created in the window. Use it for daily sales exports.
  • lastModifiedDate — rows that changed in the window, such as status updates, captures and late declines. Use it for incremental reconciliation, so you pick up transactions created earlier whose state changed since your last run.

#Timezone

Exirom stores timestamps in platform time (Europe/Nicosia, UTC+2 / UTC+3 in summer). With timezone set, the platform:

  • interprets dateFrom and dateTo in your timezone, and
  • renders the platform timestamps in the file (created date, last modified date) in that same timezone. orderDate is shown exactly as you sent it.

For example, "timezone": "UTC" with "dateFrom": "2026-07-01 00:00:00" and "dateTo": "2026-07-01 23:59:59" returns exactly the UTC day of 1 July, and the file shows UTC times. Without timezone, both the range and the file use Europe/Nicosia.

{
  "dateFrom": "2026-07-01 00:00:00",
  "dateTo": "2026-07-01 23:59:59",
  "sourceType": "APM_PAYMENT",
  "dateSource": "lastModifiedDate",
  "timezone": "UTC",
  "format": "CSV"
}

#Response 200 OK

FieldTypeDescription
idstringReport ID — use it to poll and download
sourceTypestringRequested report type
statusstringIN_PROGRESS on creation
fileNamestringGenerated file name
fileSizestring | nullFile size in bytes; null until ready
createdAtstringYYYY-MM-DDTHH:MM:SS.sss, in platform time (Europe/Nicosia) whatever timezone is
updatedAtstring | nullLast status change, in platform time; null until the status changes
{
  "id": "4821",
  "sourceType": "APM_PAYMENT",
  "status": "IN_PROGRESS",
  "fileName": "report_APM_PAYMENT_20260702080000.csv",
  "fileSize": null,
  "createdAt": "2026-07-02T08:00:00.000",
  "updatedAt": null
}

#2. Poll the status

GET https://sandbox.api.exirom.com/api/v1/report-generator/s2s/async/{id}

Returns the same object as the submit call. Watch status:

statusMeaning
IN_PROGRESSStill generating — poll again
READYDone — fetch the download URL
FAILEDGeneration failed — submit a new request

Poll every 5–10 seconds. Small ranges are usually ready in seconds; large ranges can take a few minutes.


#3. Download the file

GET https://sandbox.api.exirom.com/api/v1/report-generator/s2s/async/{id}/download-url

Call this once status is READY; before that it returns 400. It returns a temporary pre-signed URL that you download with a plain GET (no auth headers needed). The link expires, so download right away and request a new URL if needed.

{ "url": "https://reports.example.com/report_APM_PAYMENT_20260702080000.csv?X-Amz-Signature=..." }

#Full example

BASE='https://sandbox.api.exirom.com/api/v1/report-generator/s2s/async'
AUTH=(-H "x-merchant-key: $MERCHANT_KEY" -H "x-merchant-secret: $MERCHANT_SECRET")
 
set -euo pipefail
 
# 1. Submit
ID=$(curl -fsS "$BASE" "${AUTH[@]}" -H 'Content-Type: application/json' \
  --data '{"dateFrom":"2026-07-01 00:00:00","dateTo":"2026-07-01 23:59:59","sourceType":"CARD_PAYMENT","timezone":"UTC"}' \
  | jq -er .id)
 
# 2. Poll
STATUS=IN_PROGRESS
while [ "$STATUS" = "IN_PROGRESS" ]; do
  sleep 5
  STATUS=$(curl -fsS "$BASE/$ID" "${AUTH[@]}" | jq -er .status)
done
[ "$STATUS" = "READY" ] || { echo "Report $ID is $STATUS" >&2; exit 1; }
 
# 3. Download
URL=$(curl -fsS "$BASE/$ID/download-url" "${AUTH[@]}" | jq -er .url)
curl -fsS -o report.csv "$URL"

#Synchronous endpoint (deprecated)

POST /api/v1/report-generator/s2s (deprecated) takes the same body (including sourceType, dateSource and timezone) and returns the file directly as a binary stream with a Content-Disposition: attachment header. If the report fails or is not ready in time, it returns 422. Large ranges can hit HTTP timeouts on your side or ours. To migrate:

  1. Send the same body to POST /api/v1/report-generator/s2s/async.
  2. Poll GET …/async/{id} until READY.
  3. Download from the URL returned by GET …/async/{id}/download-url.

#Errors

HTTPWhen
400Invalid x-merchant-key / x-merchant-secret, missing or malformed dateFrom/dateTo, a range longer than 10 days, or unsupported format, sourceType, dateSource or timezone
400download-url called before the report is READY
404Report ID not found or created by another merchant
422Deprecated synchronous endpoint only: the report failed or was not ready in time
{ "error": "Invalid merchant credentials" }

#Best Practices

  • Automate it. Run the export from a scheduled job (for example nightly reconciliation), not interactively.
  • Prefer lastModifiedDate for incremental syncs. Each run, request the window since your previous run so status changes are not missed.
  • Pin a timezone. Pass UTC (or your accounting timezone) so day boundaries and file dates match your books.
  • Match the portal. Report columns mirror the Merchant Portal's Export Transactions download for the same report type.
  • Keep credentials server-side. Never embed x-merchant-secret in client apps.

Was this helpful?