Currency Conversion Field

Create fields that automatically convert a record's currency value into other currencies using daily exchange rates.


A Currency Conversion field automatically converts the value of a source Currency field into one or more target currencies. Whenever the source value changes, Blue looks up the exchange rate and writes the converted amount onto the conversion field — you never set its value by hand.

Currency Conversion fields are CustomField objects with type: CURRENCY_CONVERSION. You create them with the createCustomField mutation and define their currency pairs with createCustomFieldOptions. Exchange rates come from the Frankfurter API, which blends daily reference rates from the European Central Bank and 50+ other central banks and institutions, with history back to 1999.

All examples call https://api.blue.app/graphql and require the standard auth headers (blue-token-id, blue-token-secret, blue-org-id) plus blue-workspace-id — custom fields are scoped to the workspace named in that header. Headers are case-insensitive. See Authentication for details.

Overview

A working conversion setup has three pieces:

  1. A source field of type CURRENCY that holds the original amount and currency.
  2. A CURRENCY_CONVERSION field whose currencyFieldId points at that source field.
  3. One or more conversion options on the conversion field, each defining a from → to currency pair.

When a record’s source Currency value is set, Blue finds every Currency Conversion field that references it, matches an option by source currency (falling back to an Any option if present), fetches the rate, and stores the converted amount on the record.

Create

Creating the conversion field and its options takes three calls.

Step 1 — Create the source Currency field

mutation CreateSourceCurrencyField {
  createCustomField(input: { name: "Contract Value", type: CURRENCY, currency: "USD" }) {
    id
    name
    type
  }
}

Save the returned id — it becomes the currencyFieldId in the next step.

Step 2 — Create the Currency Conversion field

mutation CreateConversionField {
  createCustomField(
    input: {
      name: "Contract Value (Converted)"
      type: CURRENCY_CONVERSION
      currencyFieldId: "field_123"
      conversionDateType: "currentDate"
    }
  ) {
    id
    name
    type
    currencyFieldId
    conversionDateType
  }
}

currencyFieldId is the id of the source Currency field from Step 1. conversionDateType controls which day’s rate is used (see Conversion date types below). Save the returned id for Step 3.

Step 3 — Define the conversion options

Each option declares a currency pair. Use createCustomFieldOptions to add several at once: the customFieldId is given once on the wrapper, and each entry in customFieldOptions is a CustomFieldOptionInput (which carries no customFieldId of its own).

mutation CreateConversionOptions {
  createCustomFieldOptions(
    input: {
      customFieldId: "field_123"
      customFieldOptions: [
        { title: "USD to EUR", currencyConversionFrom: "USD", currencyConversionTo: "EUR" }
        { title: "USD to GBP", currencyConversionFrom: "USD", currencyConversionTo: "GBP" }
        { title: "Any to JPY", currencyConversionFrom: "Any", currencyConversionTo: "JPY" }
      ]
    }
  ) {
    id
    title
    currencyConversionFrom
    currencyConversionTo
  }
}

To add a single option later, use createCustomFieldOption instead — its input (CreateCustomFieldOptionInput) carries the customFieldId on each call:

mutation AddFallbackOption {
  createCustomFieldOption(
    input: {
      customFieldId: "field_123"
      title: "Any currency to EUR"
      currencyConversionFrom: "Any"
      currencyConversionTo: "EUR"
    }
  ) {
    id
    title
  }
}
Two option input shapes

createCustomFieldOptions (plural) wraps one customFieldId plus an array of CustomFieldOptionInput. createCustomFieldOption (singular) takes a single CreateCustomFieldOptionInput with customFieldId on each call. Don’t put a per-option customFieldId inside the plural call’s array — it isn’t a field there.

Parameters

CreateCustomFieldInput (Currency Conversion fields)

Only the fields relevant to CURRENCY_CONVERSION are listed. Custom fields are scoped to the workspace in the blue-workspace-id header — there is no projectId parameter.

ParameterTypeRequiredDescription
nameString!YesDisplay name of the field.
typeCustomFieldType!YesMust be CURRENCY_CONVERSION.
currencyFieldIdStringYes (for conversion)id of the source CURRENCY field to convert from. Stored as an empty string if omitted, leaving the field with nothing to convert.
conversionDateTypeStringNoWhich day’s rate to use. See Conversion date types. Defaults to current-date behavior when omitted.
conversionDateStringNoCompanion value for conversionDateType — an ISO date for specificDate, or "todoDueDate" / a DATE field id for fromDateField.
descriptionStringNoHelp text shown alongside the field.

CreateCustomFieldOptionsInput (the createCustomFieldOptions wrapper)

ParameterTypeRequiredDescription
customFieldIdString!Yesid of the CURRENCY_CONVERSION field these options belong to.
customFieldOptions[CustomFieldOptionInput!]!YesThe options to create.

CustomFieldOptionInput (each entry in customFieldOptions)

ParameterTypeRequiredDescription
titleString!YesDisplay label for the conversion pair.
currencyConversionFromStringYes (for conversion)Source ISO currency code (e.g. "USD"), or the literal "Any" for a fallback. Schema-optional; required for a usable conversion option.
currencyConversionToStringYes (for conversion)Target ISO currency code (e.g. "EUR"). Schema-optional; required for a usable conversion option.
colorStringNoHex color for the option.
positionFloatNoSort position; appended to the end if omitted.

CreateCustomFieldOptionInput (the createCustomFieldOption singular input)

Same as CustomFieldOptionInput, plus:

ParameterTypeRequiredDescription
customFieldIdString!Yesid of the CURRENCY_CONVERSION field.
todoIdStringNoRecord context for the option-created event.

Conversion date types

conversionDateType is a plain String in the schema, not an enum. The following values are the ones the conversion engine recognizes; anything else (or omitting it) falls back to current-date behavior.

ValueRate usedconversionDate
currentDateThe rate as of the day the source value was last updated.Not used.
specificDateThe rate on a fixed historical date.ISO date string, e.g. "2024-01-01T00:00:00Z" (only the date portion is used).
fromDateFieldThe rate on a date read from another field."todoDueDate" for the record’s due date, or the id of a DATE custom field.

Response

createCustomField returns the created CustomField:

{
  "data": {
    "createCustomField": {
      "id": "clm4n8qwx000008l0g4oxdqn7",
      "name": "Contract Value (Converted)",
      "type": "CURRENCY_CONVERSION",
      "currencyFieldId": "clm4n8abc000008l0sourcefld",
      "conversionDateType": "currentDate"
    }
  }
}

createCustomFieldOptions returns the created CustomFieldOption list:

{
  "data": {
    "createCustomFieldOptions": [
      {
        "id": "clm4n8opt000008l0usd2eur00",
        "title": "USD to EUR",
        "currencyConversionFrom": "USD",
        "currencyConversionTo": "EUR"
      }
    ]
  }
}

Set a value

You never write the converted amount directly — a Currency Conversion field is read-only. Instead, set the source CURRENCY field with setRecordCustomField, passing number and currency. Every conversion field that references the source then recomputes automatically.

mutation SetSourceCurrency {
  setRecordCustomField(
    input: { todoId: "todo_123", customFieldId: "field_123", number: 1000, currency: "USD" }
  )
}

setRecordCustomField returns Boolean!, so it takes no sub-selection:

{ "data": { "setRecordCustomField": true } }

After this call, a USD → EUR conversion field on the same record holds the EUR equivalent of 1000 USD; a USD → GBP field holds the GBP equivalent, and so on.

Read a value

The converted amount is read off the conversion CustomField in record context — there is no separate value type. Select number (the converted amount), currency (the target code), and optionally the structured value JSON via Record.customFields:

query ReadConvertedValue {
  recordQueries {
    todos(
      filter: { companyIds: ["company_123"], projectIds: ["project_123"], todoIds: ["todo_123"] }
    ) {
      items {
        id
        customFields {
          id
          name
          type
          number
          currency
          currencyFieldId
          conversionDateType
          conversionDate
          value
        }
      }
    }
  }
}
{
  "data": {
    "recordQueries": {
      "todos": {
        "items": [
          {
            "id": "clm4n8qwx000008l0g4oxdqn7",
            "customFields": [
              {
                "id": "clm4n8opt000008l0convfield",
                "name": "Contract Value (Converted)",
                "type": "CURRENCY_CONVERSION",
                "number": 925.4,
                "currency": "EUR",
                "currencyFieldId": "clm4n8abc000008l0sourcefld",
                "conversionDateType": "currentDate",
                "conversionDate": "",
                "value": {
                  "number": 925.4,
                  "currency": "EUR",
                  "currencyFieldId": "clm4n8abc000008l0sourcefld",
                  "conversionDateType": "currentDate",
                  "conversionDate": ""
                }
              }
            ]
          }
        ]
      }
    }
  }
}

Errors

These codes apply to the create and set operations on this page (each verified against the API’s error definitions).

CodeOperationWhen
CUSTOM_FIELD_NOT_FOUNDcreateCustomFieldOption(s), setRecordCustomFieldThe customFieldId doesn’t exist or isn’t in a workspace you can access.
TODO_NOT_FOUNDsetRecordCustomFieldThe todoId doesn’t exist or isn’t accessible.
FORBIDDENcreateCustomField, createCustomFieldOptionThe caller isn’t an OWNER or ADMIN on the workspace.
BAD_USER_INPUTsetRecordCustomFieldThe field type can’t be set this way, or a referenced option/value is invalid.
PLAN_LIMIT_REACHEDcreateCustomFieldThe workspace has hit its custom-field limit, or the organization is locked.

Notes

  • Read-only. A CURRENCY_CONVERSION field is computed from its source Currency field. setRecordCustomField on the conversion field itself does nothing — set the source field instead. See Set Custom Field Values.
  • Any fallback. An option with currencyConversionFrom: "Any" is used when no option matches the source currency exactly, letting one conversion field handle records denominated in different currencies.
  • Same-currency shortcut. When the matched pair has identical from and to currencies, the amount is copied through without calling the rate API.
  • On failure, the amount is 0. If the rate API is unavailable or returns no rate (e.g. an unsupported currency code), the converted number is stored as 0 with the target currency still set. No error is surfaced to the caller.
  • No conversion runs when the source field has no amount, or when no option matches and no Any fallback exists.
  • Crypto and custom rates aren’t supported. Frankfurter covers fiat currencies only; there’s no way to supply your own rate, and crypto codes (BTC, ETH) can be entered in a Currency field but not converted.