Skip to main content

Squillify development is paused indefinitely. Read why, and discover what this experiment achieved. Read the story

MicroSkills Reference

Apply FX Rates

finance.fx_rate_apply

ProductionBounded custom (Class B)Deterministicstrictfastv1.0.0
No network

Overview

You send amounts, a target currency and your own rate table; we convert each amount and report the rate we used, whether it was direct, inverted or routed through your pivot currency, and how old it was. We never fetch, guess or infer a rate. Inversion and triangulation are off until you turn them on, and anything we cannot convert comes back listed with a reason instead of failing the whole batch.

Converting with a rate somebody fetched at an unknown moment, and inverting it quietly when the pair is missing, is how a reporting pack stops matching the treasury's own numbers. You supply the rates; we tell you exactly which one hit each line, how it was derived and how old it was.

Reliability

Rates come only from your table, and resolution is tried in one published order: same currency, then direct, then inverted, then two hops through your pivot. A derived rate is capped at 12 decimals and the value we publish is the value we multiplied by, so you can reproduce every line; when capping changed the rate you get a PRECISION_TRUNCATED warning. FX_RATE_TABLE_INVALID — trigger: an empty table, or an entry with a missing or unsupported currency code, the same currency on both sides, a rate we cannot read, a rate finer than 12 decimals, a malformed asOf date, or a property we do not define; retryable: no, the same table always fails; next: fix the rate at the position we name in the details. FX_RATE_DUPLICATE_PAIR — trigger: two entries give a rate for the same pair, so which one applies would be arbitrary; retryable: no; next: keep one rate per pair per call and split the batch if you genuinely need two. FX_RATE_NON_POSITIVE — trigger: a rate of zero or a negative rate, neither of which is a ratio between currencies; retryable: no; next: send the rate as a positive decimal such as "0.92". FX_PIVOT_UNKNOWN — trigger: a pivot currency we do not support, or one your rate table never mentions, so no amount could be routed through it; retryable: no; next: add the pivot legs to the table, or leave pivotCurrency out. FX_LIMIT_EXCEEDED — trigger: more than 5,000 amounts, or more than 380 rates, which is every ordered pair the 20 supported codes can form; retryable: no; next: send the amounts in batches, reusing the same table. Both limits are ours, not the envelope's: overshoot either by one row or by a thousand and you get this code with the limit and your actual count in the details. CURRENCY_UNKNOWN — trigger: a target currency, or an amount's currency, that is not one of the supported ISO 4217 codes; retryable: no; next: send a supported code. AMOUNT_INVALID — trigger: an empty amounts list, an amount we cannot read as a plain decimal, or one with more than 12 decimals; retryable: no; next: send amounts as plain decimal strings without separators or exponents. AMOUNT_MAGNITUDE_EXCEEDED — trigger: an amount beyond the 1E18 cap we hold exactly; retryable: no; next: scale the batch to thousands and read the result in the same unit.

Context footprint

One row per amount plus a warning per derivation, so a 500-line ledger comes back as a flat list you can page through. A successful call costs 1 execution credit.

When to use it

When you already hold the rates you are contractually meant to use and want them applied consistently across a batch.

When you need current market rates, or a route through several currencies that you have not chosen yourself.

Scope

Note
Applies a caller-supplied rate table to a batch of amounts, declaring how every rate was derived.

Non-goals

  • Does not fetch, look up or infer a rate from anywhere
  • Does not invert or triangulate unless you ask for it
  • Does not search for a path longer than two hops
  • Does not read the clock — staleness is judged against the date you send

Properties

IdempotentRetry safeStreaming supportedFile input supportedRequires external service

Capabilities

data-transformationmath

Cost & billing

Flat rate per invocation

MetercallsBillable onsuccessCost per call1 credit

File handling

Accepts file_refProduces file_refMax input1 MB

How we handle your data

Sensitive output possible
What this means

This skill may receive sensitive content and may return submitted, transformed, extracted, or reformatted content.

Squillify guarantees
  • Runs inside Squillify
  • This skill runs inside Squillify.
  • Payloads are not sent to an external service by this skill.
  • Squillify does not permanently store submitted payloads.
  • Telemetry records metadata only, not submitted content.
  • Submitted payloads are not used for training.
  • Submitted payloads are not reviewed by humans.
  • This skill does not target sensitive data.
  • The response may contain submitted or transformed content.
Your responsibility

Squillify does not determine whether submitted content is sensitive. Your application is responsible for deciding what data it is allowed to submit. Your application is responsible for securing the response.

Read the Microskill Data Handling Policy

Input schema

FieldTypeRequiredDescription
amountsarrayYesFrom 1 through 5,000 closed amounts, each with amount, currency, and an optional join id.
targetCurrencystringYesISO 4217 code every converted figure is expressed in; its minor units set default rounding.
rateTablearrayYesFrom 1 through 380 closed directed rates, one entry per from/to pair, covering every ordered pair the 20 supported codes can form.
allowInversebooleanNoWhether a missing A to B rate may be derived as 1 divided by B to A; omit it for false.
pivotCurrencystringNoISO 4217 code to route through for at most two hops; omit it and no triangulation happens.
asOfLimitstringNoDate as YYYY-MM-DD; older rates are warned and still applied. Omit it and nothing is judged on age.
roundingobjectNoOptional rounding for converted amounts; omit it and the target currency's minor units decide.

Output schema

FieldTypeRequiredDescription
convertedarrayYesOne row per amount that converted, in input order, each with rateUsed, mode, path, and optional asOf.
warningsarrayYesDerivation and staleness notes, empty when every conversion was a direct untruncated in-date rate.
unconvertedarrayYesAmounts that had no usable rate, each with position, sourceCurrency, and reasonCode; never discards conversions that worked.
targetCurrencystringYesISO 4217 unit every converted figure is expressed in, repeated so a stored conversion names its target.
roundingobjectYesMode and scale actually applied to every converted amount after the target currency filled omitted fields.

API contract

POST /v1/execute
curl
curl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"finance.fx_rate_apply","input":{"amounts":[{"id":"A1","amount":"1000.00","currency":"USD"}],"targetCurrency":"EUR","rateTable":[{"from":"USD","to":"EUR","rate":"0.92","asOf":"2026-07-27"}]}}'
Integrations
TypeScript SDK
import { Squillify } from "@squillify/sdk";

const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });

const { output } = await sq.execute("finance.fx_rate_apply", {"targetCurrency":"EUR","amounts":[{"id":"A1","amount":"1000.00","currency":"USD"},{"id":"A2","amount":"250.00","currency":"EUR"}],"rateTable":[{"from":"USD","to":"EUR","rate":"0.92","asOf":"2026-07-27"}]});

Errors

CodeHTTPRetryableDescription
VALIDATION_ERROR400NoThe request payload failed schema validation.
UNAUTHORIZED401NoMissing or invalid API key.
SKILL_NOT_ACCESSIBLE403NoThe API key is not granted access to this skill.
SKILL_NOT_AVAILABLE_FOR_PLAN403NoThis skill is not included in your current plan.
PLAN_ELIGIBILITY_UNAVAILABLE503YesPlan eligibility could not be decided. Retry after backoff.
SKILL_NOT_FOUND404NoThe requested skill or version does not exist.
TENANT_RATE_LIMITED429YesRequest rate limit exceeded. Retry after backoff.
QUOTA_EXCEEDED429YesPlan quota exhausted. Retryable once the quota period resets.
EXECUTION_TIMEOUT504YesExecution exceeded the allowed time budget.
FX_RATE_TABLE_INVALID400NoThe request payload failed this skill's input validation.
FX_RATE_DUPLICATE_PAIR400NoThe request payload failed this skill's input validation.
FX_RATE_NON_POSITIVE400NoThe request payload failed this skill's input validation.
FX_PIVOT_UNKNOWN400NoThe request payload failed this skill's input validation.
FX_LIMIT_EXCEEDED429NoA size, quota, rate, or time limit was exceeded.
CURRENCY_UNKNOWN400NoThe request payload failed this skill's input validation.
AMOUNT_INVALID400NoThe request payload failed this skill's input validation.
AMOUNT_MAGNITUDE_EXCEEDED429NoA size, quota, rate, or time limit was exceeded.

Other limits

LimitValue
Recommended inline bytes32 KiB

Limitations

  • Does not fetch, look up or infer a rate from anywhere
  • Does not invert or triangulate unless you ask for it
  • Does not search for a path longer than two hops
  • Does not read the clock — staleness is judged against the date you send
  • Live or market rates, which we have no way to obtain
  • Rate-path discovery across a graph of currencies
  • Deciding whether a rate is too old to use

How to call

POST /v1/execute (requires authentication)

Request bodyjson
{
  "skillKey": "finance.fx_rate_apply",
  "input": {
    "targetCurrency": "EUR",
    "amounts": [
      {
        "id": "A1",
        "amount": "1000.00",
        "currency": "USD"
      },
      {
        "id": "A2",
        "amount": "250.00",
        "currency": "EUR"
      }
    ],
    "rateTable": [
      {
        "from": "USD",
        "to": "EUR",
        "rate": "0.92",
        "asOf": "2026-07-27"
      }
    ]
  }
}

Examples

A dollar amount converted at your published rate, a euro one passed through

Inputjson
{
  "targetCurrency": "EUR",
  "amounts": [
    {
      "id": "A1",
      "amount": "1000.00",
      "currency": "USD"
    },
    {
      "id": "A2",
      "amount": "250.00",
      "currency": "EUR"
    }
  ],
  "rateTable": [
    {
      "from": "USD",
      "to": "EUR",
      "rate": "0.92",
      "asOf": "2026-07-27"
    }
  ]
}
Expected outputjson
{
  "warnings": [],
  "converted": [
    {
      "id": "A1",
      "sourceAmount": "1000",
      "sourceCurrency": "USD",
      "convertedAmount": "920.00",
      "rateUsed": "0.92",
      "mode": "DIRECT",
      "path": [
        "USD",
        "EUR"
      ],
      "asOf": "2026-07-27"
    },
    {
      "id": "A2",
      "sourceAmount": "250",
      "sourceCurrency": "EUR",
      "convertedAmount": "250.00",
      "rateUsed": "1",
      "mode": "IDENTITY",
      "path": [
        "EUR"
      ]
    }
  ],
  "unconverted": [],
  "rounding": {
    "scale": 2,
    "mode": "HALF_UP"
  },
  "targetCurrency": "EUR"
}

An inverted rate is flagged, and a missing pair is reported not fatal

Inputjson
{
  "rateTable": [
    {
      "from": "USD",
      "to": "EUR",
      "rate": "0.92"
    }
  ],
  "allowInverse": true,
  "targetCurrency": "USD",
  "amounts": [
    {
      "id": "B1",
      "amount": "500.00",
      "currency": "EUR"
    },
    {
      "id": "B2",
      "amount": "300.00",
      "currency": "GBP"
    }
  ]
}
Expected outputjson
{
  "warnings": [
    {
      "code": "RATE_INVERTED",
      "position": 1,
      "message": "No direct rate was given for this pair, so we used 1 divided by the reverse rate."
    },
    {
      "code": "PRECISION_TRUNCATED",
      "position": 1,
      "message": "The rate we derived does not fit in 12 decimals, so we rounded it and converted with the rounded value shown in rateUsed."
    }
  ],
  "converted": [
    {
      "id": "B1",
      "sourceAmount": "500",
      "sourceCurrency": "EUR",
      "convertedAmount": "543.48",
      "rateUsed": "1.086956521739",
      "mode": "INVERTED",
      "path": [
        "EUR",
        "USD"
      ]
    }
  ],
  "unconverted": [
    {
      "id": "B2",
      "position": 2,
      "sourceCurrency": "GBP",
      "reasonCode": "NO_RATE_FOR_PAIR"
    }
  ],
  "rounding": {
    "scale": 2,
    "mode": "HALF_UP"
  },
  "targetCurrency": "USD"
}
  • Restate a multi-currency ledger in your reporting currency at month-end rates
  • Convert a supplier's quote using the rate your treasury desk published
  • Show a client the exact rate and derivation behind every converted line

Avoid for

  • Live or market rates, which we have no way to obtain
  • Rate-path discovery across a graph of currencies
  • Deciding whether a rate is too old to use

Composable with

Safety & privacy

  • Runs without calling any external service.
  • Your payloads are never stored permanently.
  • Telemetry captures metadata only — never your payload contents.
  • We never train models on your payloads.
  • No human reviews your payloads.

Tags

mathtransformationcurrencyfinancefx