CorebanqCorebanq Developer Docs
FXv2

Preview spread rates

Preview BUY/SELL rates that would be applied for a given currency pair, tariff, and MID rate

GET
/v2/fx/spreads/preview

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Query Parameters

fx_tariff_id?string

FX tariff ID (optional)

provider?string

Rate provider (optional)

base_currency*string

Base currency code

target_currency*string

Target currency code

mid_rate*number

MID rate to apply spreads to

at?string

Timestamp for which to resolve spreads (ISO 8601)

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/v2/fx/spreads/preview?base_currency=string&target_currency=string&mid_rate=0"
{
  "mid_rate": 1.0923,
  "bid_rate": 1.0878,
  "ask_rate": 1.0968,
  "bid_spread_bps": 50,
  "ask_spread_bps": 50,
  "applied_rule_ids": [
    "c3f728eb-7f89-44c5-8a0d-db30827f9b2e"
  ],
  "spread_rule_id": "uuid1"
}
{
  "status": 409,
  "message": "Spread rule overlaps an existing rule",
  "code": "fx_m.spread_overlap",
  "class": "business",
  "retryable": false,
  "details": [
    {
      "field": "percent_fee",
      "rule": "lte",
      "param": "1",
      "message": "string"
    }
  ]
}
{
  "status": 409,
  "message": "Spread rule overlaps an existing rule",
  "code": "fx_m.spread_overlap",
  "class": "business",
  "retryable": false,
  "details": [
    {
      "field": "percent_fee",
      "rule": "lte",
      "param": "1",
      "message": "string"
    }
  ]
}
{
  "status": 409,
  "message": "Spread rule overlaps an existing rule",
  "code": "fx_m.spread_overlap",
  "class": "business",
  "retryable": false,
  "details": [
    {
      "field": "percent_fee",
      "rule": "lte",
      "param": "1",
      "message": "string"
    }
  ]
}
{
  "status": 429,
  "message": "Rate limit for 203.0.113.7 to GET:/v1/fx/rates exceeded.",
  "code": "rate_limits_m.exceeded",
  "class": "temporary",
  "retryable": true
}
{
  "status": 409,
  "message": "Spread rule overlaps an existing rule",
  "code": "fx_m.spread_overlap",
  "class": "business",
  "retryable": false,
  "details": [
    {
      "field": "percent_fee",
      "rule": "lte",
      "param": "1",
      "message": "string"
    }
  ]
}

{
  "overall_status": "unhealthy",
  "message": "Service is shutting down",
  "timestamp": "2026-08-27T15:04:05Z"
}

GETGet spread rule history

Retrieve the change history for a specific spread rule

GETList FX tariffs

Lists FX tariffs in the shared list envelope. Items are transform.FxTariffResponse, which does NOT carry is_default — the v1 CRUD shape does. Results are limited to the caller's permitted records: the service passes a real caller id and RbacRecordType into the shared getAll path, which folds the permitted ids into the query. SEARCH FILTERS. Every search.* parameter below is one filter on one column, and the list is the whole set the route accepts. Append an operator to compare instead of match — search.{field}.{op}, op one of eq, ne, gt, gte, lt, lte, in, nin, like, start_with, end_with; with no suffix the operator is eq. in and nin take a comma-separated list. like, start_with and end_with are case-insensitive ILIKE. search.{field}.eq=null and search.{field}.ne=null test for NULL; any other operator with an empty value is 400 query.operator_requires_value. A STRING VALUE IS SILENTLY REWRITTEN BEFORE IT REACHES THE QUERY. sanitizeStringValue drops every character that is not a letter, a digit, or one of . / @ - _ and space, and it never reports an error — so search.provider=A*B filters on AB and answers 200, not 400. A value that parses as a timestamp is passed through instead of stripped. Which rejections are 400 and which are 500 is set out under the 400 and 500 responses; the split is not the one a caller would expect. A DATE-TYPED FILTER IS TRUNCATED TO THE DAY. Where the route's field map types a column as date, normalizeSearchValueByFieldType rewrites the value as YYYY-MM-DD at midnight UTC before it reaches SQL, so a time component on such a filter is discarded without a word. Those parameters carry format: date below; the format: date-time ones are compared as sent.