1. Sokin MCP
Sokin Embedded API
  • Getting Started
    • Sokin Definitions
    • Guides
      • Authentication
      • Receiving Payments
      • FX Payments
      • Payments
      • Unfunded Payments
      • Unfunded FX Payments
      • Webhooks
      • Corporate Onboarding
        • Data Requirements
        • Step 1: Create a corporate profile
        • Step 2: Add associates
        • Step 3: Add a parent entity (if applicable)
        • Step 4: Upload company documents
        • Step 5: Upload associate documents
        • Real-time Status Updates via Webhook
        • Conditional Step: Upload Parent Entity documents (if applicable)
        • Step 6: Finalise onboarding submission
        • Uploading supporting documents using Pre-signed URLs
        • Onboarding Reference Data
        • Onboarding Models
          • Data-Only model
          • Supported Jurisdictions & National ID Requirements
      • Sokin MCP
        • Sokin MCP - Setup and Usage Guide
        • Sokin MCP - Tool Guide
        • Sokin MCP - Technical Docs
  • Authentication
    • Get Token
      POST
    • Schemas
      • TokenResponse
  • Corporates
    • v2025-12-01
      • Create a new Corporate
      • Add a parent entity (corporate associate) to an onboarding request
      • Add an associate to a corporate onboarding request
      • Request pre-signed URLs for onboarding document uploads
      • Request pre-signed URLs for parent entity document uploads
      • Request pre-signed URLs for associate document uploads
      • Finalise corporate onboarding submission
    • Schemas
      • CreateCorporateRequest
      • UboDetails
      • OwnershipType
      • AssociateDocumentPresignedUrlResponse
      • AddOnboardingDocumentsRequest
      • MessageResponse
      • AssociateDocumentPresignedUrlResponseItem
      • ErrorDetail
      • CorporateAssociateType
      • ErrorResponse
      • PresignedUrlResponse
      • AddressTypeEnum
      • FinaliseSubmissionRequest
      • AddParentEntityRequest
      • CreateCorporateResponse
      • AssociateIdentityType
      • ApiResponse[AddOnboardingDocumentsResponse]
      • CorporateCategories
      • AddOnboardingDocumentsResponse
      • ApiResponse[CorporateDetailResponse]
      • ApiResponse[AddAssociateDocumentsResponse]
      • AddParentEntityDocumentsRequest
      • AddAssociateDocumentsResponse
      • AddParentEntityResponse
      • AddIndividualAssociateRequest
      • ApiResponse[CreateCorporateResponse]
      • AddIndividualAssociateResponse
      • CorporateDetailResponse
      • AssociateType
      • ApiResponse[AddParentEntityResponse]
      • NumericRange
      • ApiResponse[AddIndividualAssociateResponse]
      • MoneyRange
      • AddAssociateDocumentsRequest
      • FinaliseSubmissionResponse
      • AssociateIdentityType
  • Corporate Currency Accounts
    • v2025-12-01
      • Get Corporate Currency Accounts
      • Get Cca Ledger Items
      • Get Corporate Currency Account By Reference
    • Schemas
      • GetCCAByReferenceResponse
      • GetCCAsResponseItem
      • PaginatedApiResponse[GetCCALedgerItemsResponseItem]
      • ErrorDetail
      • PageInfo
      • SearchCCALedgersResponseItem
      • PaginationInfo
      • GetCCAUnifiedBalanceResponse
      • PaginatedApiResponse[SearchCCALedgersResponseItem]
      • ErrorResponse
      • ApiResponse[GetCCAUnifiedBalanceResponse]
      • SearchCCALedgersRequest
      • PaginatedApiResponse[GetCCAsResponseItem]
      • ApiResponse[GetCCAByReferenceResponse]
      • PaginationInfo
      • SearchCCALedgersPagination
      • GetCCAByReferenceResponsePayInDetail
      • CurrencyCode
      • GetCCALedgerItemsResponseItem
      • PaginationInfo
  • Beneficiaries
    • v2025-12-01
      • Get Beneficiary Schema
      • Create Beneficiary
      • Get Beneficiary List
      • Get Beneficiary Details
      • Delete Beneficiary
      • Validate Beneficiary
      • Validate Beneficiaries
      • Acknowledge Validation Mismatch
      • List Financial Institutions
      • List Financial Institution Branches
    • Schemas
      • AcknowledgeValidationMismatchRequest
      • ValidationType
      • ProviderBankIdentifierRequest
      • ErrorDetail
      • FinancialInstitutionReferenceResponse
      • AcknowledgeValidationMismatchResponse
      • ApiResponse[AcknowledgeValidationMismatchResponse]
      • ApiResponse[list[FinancialInstitutionReferenceResponse]]
      • AcknowledgedValidationMismatchDestinationResponse
      • DeleteBeneficiaryData
      • AddressResponse
      • BeneficiarySchemaFieldCondition
      • StateOrProvince
      • ApiResponse[list[BankCountryCurrencyResponse]]
      • PageInfo
      • BeneficiarySchemaField
      • BeneficiaryValidationResultResponse
      • StateOrProvince
      • ApiResponse[BeneficiaryResponse]
      • BeneficiaryField
      • RegionalHints
      • BeneficiaryStatus
      • DestinationResponse
      • ValidationStatus
      • BeneficiaryType
      • PaymentMethodType
      • CreateBeneficiaryRequest
      • PaginatedApiResponse[FinancialInstitutionBranchReferenceResponse]
      • ValidateBeneficiariesRequest
      • ApiResponse[ValidateBatchBeneficiariesResponse]
      • CompanyDetailsResponse
      • SchemaFieldVisibility
      • FieldErrorResponse
      • RoutingScheme
      • StateOrProvince
      • ValidateBeneficiaryRequest
      • ErrorResponse
      • FieldType
      • VirtualAccountDestinationRequest
      • ApiResponse[DeleteBeneficiaryData]
      • SokinInternalDestinationRequest
      • GetBeneficiaryFieldsResponse
      • BeneficiaryResponse
      • ApiResponse[ValidateBeneficiaryResponse]
      • FinancialInstitutionBranchReferenceResponse
      • IdentifierType
      • IndividualDetailsRequest
      • SchemaFieldType
      • CurrencyCode
      • TransactionTypeEnum
      • CryptoDestinationRequest
      • AccountType
      • BeneficiaryValidationPayload
      • ApiResponse[GetBeneficiaryFieldsResponse]
      • AccountNumberType
      • PatchBeneficiaryRequest
      • BankAccountCategory
      • ApiResponse[GetBeneficiarySchemaResponse]
      • PaginatedApiResponse[BeneficiaryResponse]
      • ApiResponse[list[BankCountryResponse]]
      • AddressRequest
      • PaginationInfo
      • BankCountryCurrencyResponse
      • PaginationInfo
      • ValidateBatchBeneficiariesResponse
      • PaymentRail
      • CreateExternalBeneficiaryData
      • FinancialInstitutionRequest
      • ApiResponse[CreateExternalBeneficiaryData]
      • CompanyDetailsRequest
      • BankCountryResponse
      • CreateExternalBeneficiaryRequest
      • BankAccountDestinationRequest
      • IndividualDetailsResponse
      • BeneficiarySchemaCondition
      • BeneficiaryDestinationValidationResult
      • BeneficiaryEntityType
      • GetBeneficiarySchemaResponse
      • ETransferDestinationRequest
      • ValidateBeneficiaryResponse
      • RoutingCodeRequest
      • ApiResponse[BeneficiaryDetails]
      • BeneficiaryListItem
      • PaginatedApiResponse[BeneficiaryListItem]
      • PaginationInfo
      • PaginatedBeneficiaryFieldsResponse
      • BeneficiaryDetails
      • ApiResponse[ListFinancialInstitutionsResponse]
      • ListFinancialInstitutionsResponse
  • Instruction Requests
    • v2025-12-01
      • Create Payment Instruction Request
      • Create Fx Instruction Request
      • Create Fx Payment Instruction Request
      • Create Unfunded Payment Instruction Request
      • Create Unfunded Fx Payment Instruction Request
      • Get Instruction Request By Reference
    • Schemas
      • ApiResponse[GetInstructionRequestResponse]
      • ValidateInstructionCreationRequest
      • ErrorDetail
      • InstructionType
      • CreateInstructionRequestResponse
      • ApiResponse[CreateInstructionRequestResponse]
      • ValidateInstructionCreationResponse
      • CreateFxPaymentInstructionRequest
      • FeeBreakdownResponse
      • ErrorResponse
      • CreateUnfundedPaymentInstructionRequest
      • CreateUnfundedFxPaymentInstructionRequest
      • ApiResponse[list[str]]
      • GetInstructionRequestResponse
      • PaymentPurpose
      • CreatePaymentInstructionRequest
      • CreateFXInstructionRequest
      • ApiResponse[ValidateInstructionCreationResponse]
      • CreateSokinDirectInstructionRequest
  • Instructions
    • v2025-12-01
      • Get Instruction By Reference
    • Schemas
      • GetInstructionResponse
      • PaginatedApiResponse[InstructionListResponseItem]
      • ApiResponse[GetInstructionResponse]
      • PaginationInfo
      • ErrorResponse
      • ErrorDetail
      • PageInfo
      • InstructionListResponseItem
      • PaginationInfo
  • Foreign Exchange
    • v2025-12-01
      • Get Fx Rate
    • v2026-08-01
      • Request FX Quote
      • Get FX Quote
    • Schemas
      • ApiResponse[FxRateData]
      • FixedSide
      • FxRateRequest
      • FXValidityPeriod
      • FxRateData
      • ApiResponse[GetQuoteResponse]
      • ErrorResponse
      • ErrorDetail
      • GetQuoteResponse
      • FXTenorType
      • FxQuoteStatus
      • ApiResponse[CreateQuoteResponse]
      • CurrencyCode
      • RequestFxQuoteRequest
      • CreateQuoteResponse
  • Webhooks
    • v2025-12-01
      • Create Subscription
      • Update Subscription
      • Get Subscription By Id
      • Delete Subscription
      • Update Subscription Status
      • List Notification Logs
      • Retry Notification
    • Schemas
      • ApiResponse[UpdateSubscriptionResponse]
      • PaginationInfo
      • SubscriptionCreateOrUpdate
      • NotificationStatus
      • NotificationAttemptResponse
      • SubscribableWebhookEventType
      • SubscriptionStatusUpdate
      • ApiResponse[CreateSubscriptionResponse]
      • UpdateSubscriptionResponse
      • PaginatedApiResponse[NotificationLogResponse]
      • PaginationInfo
      • ErrorResponse
      • ErrorDetail
      • PageInfo
      • CreateSubscriptionResponse
      • NotificationLogResponse
      • WebhookSubscription
  • Payment Acceptance
    • Payment Acceptance
  1. Sokin MCP

Sokin MCP - Tool Guide

Every tool the Sokin MCP server offers: what it is for, what it needs from
you, and an example of what comes back. 26 tools, one section each, all in
the same shape so you can skim or search the page.
Start with the Sokin MCP — Setup and Usage Guide if you have not connected a
client yet.
This page assumes you are connected and the balances prompt
worked.
You do not call these tools yourself. You ask your AI client for something in
plain English — "what are my balances", "show me last month's transactions on
the GBP account" — and it picks the tools and fills in the details. The
examples below show what it sends and receives, so you can recognise what is
happening and tell whether you got what you asked for.
Everything here describes the UAT (test) environment.

Jump to#

All 26 tools — the full list
What you can ask for — the tools grouped by job
Reading a result
Which tools your client can use
Moving money — the two flows that do, and how they ask first
The tools, one by one
Starter project ideas

All 26 tools#

In the order they appear below. Every tool's own section repeats its name as
a heading, so a browser find on the name jumps straight to it.
ToolWhat it is for
list_corporatesWhich companies your login can act for
list_accountsA company's currency accounts and balances
get_accountOne account in full, including pay-in details
get_account_ledgerTransaction history for an account
list_beneficiariesWho a company can pay
get_beneficiaryOne payee in full, with account details and verification
get_beneficiary_schemaWhat details a new payee needs
validate_beneficiaryCheck payee details, or an e-Transfer email, before relying on them
create_beneficiaryCreates a new payee, behind an emailed code
update_beneficiaryChanges an existing payee, behind an emailed code
list_financial_institutionsBanks available when setting up a payee
list_financial_institution_branchesA bank's branches and routing numbers
list_instructionsPayments and conversions, listed
get_instructionOne payment or conversion in full
get_instruction_requestHow a submitted payment actually turned out
validate_payment_instructionCheck a payment without sending it
create_payment_instructionPrepare a payment for review. Sends nothing
submit_payment_instructionSends a real payment
create_fx_payment_instructionPrepare a pay-in-another-currency payment. Sends nothing
submit_fx_payment_instructionSends a real payment, converting as it goes
get_fx_quoteOpen the quote card for converting between your own accounts (no rate without cards)
create_fx_quoteCreate a real, short-lived rate. Moves no money
settle_fx_quoteConverts real money between your own accounts
visualizeDraw a diagram or chart in the chat
get_documentation_linksWhere Sokin's documentation lives
play_cheetah_dashAn arcade game, in the chat

What you can ask for#

If you want toThe tools behind it
See which companies you can act forlist_corporates
See balances across accountslist_accounts, get_account
See transaction historyget_account_ledger
Look up who you can paylist_beneficiaries
Look up a bank or branchlist_financial_institutions, list_financial_institution_branches
Check on payments already madelist_instructions, get_instruction, get_instruction_request
Check a payment before sending itvalidate_payment_instruction
Send a paymentcreate_payment_instruction, then submit_payment_instruction
Start a conversion between your own accounts (see the rate on a card)get_fx_quote
Convert between your own accountscreate_fx_quote, then settle_fx_quote
Draw a chart or diagram of a resultvisualize
Find Sokin's documentationget_documentation_links
19 of the 26 only read data. The other seven change something, and three
of those move real money — your client asks you to confirm before any of them
runs. See Moving money.

Reading a result#

Every tool answers twice, in the same reply:
A sentence or a table — what your AI client reads out to you.
A structured payload — the same facts as data, for code to use.
The examples below show both. If you are just using a chat client, the first
one is what you will see; the second is there so a developer can check the
exact shape.
Two things worth knowing whatever you are building:
A failure comes back as an error with no structured payload at all. If
you are writing code, check for the error flag before reading the data, and
do not try to match on the wording of the message.
By default the structured payload is trimmed to the useful fields.
Clients can ask for the raw, fuller version instead; the Technical Docs
page documents that and exactly what each tool adds.
Four tools return long lists a page at a time — get_account_ledger,
list_instructions, list_beneficiaries and
list_financial_institution_branches. When there is more, the result says so
and carries a token for the next page; your client handles this for you.

Which tools your client can use#

Five tools render an interactive card in the chat rather than plain
text — a review card with Approve and Cancel for each of the two payment
pairs, an FX card with a live countdown, a chart, and a game.
Whether you get one depends on your client declaring support for them at
connect time (MCP Apps, SEP-1865). The server only draws a card for a
session that declared it
, so a client that renders widgets by some other
convention still gets the plain-text branch. Claude declares it. Replit and
ChatGPT did not as of September 2026 — check yours rather than trusting that,
since it is their behaviour to change, not ours.
This decides what you can do, not just how it looks:
Client with cardsClient without
All the read-only toolsWorkWork
get_fx_quoteInteractive quote cardNo rate and no quote, just a link to the Sokin portal to see the rate and trade
create_fx_quote, settle_fx_quoteDriven by the card's buttonsNot available
Paying a beneficiary, either pairReview card with ApproveWorks — the figures and key come back as text, and you confirm in the chat
visualize, play_cheetah_dashRenderReturn their content as text
On a client without cards, treat the read-only tools and get_fx_quote as
what you can use
— including validate_payment_instruction, which is a
genuine dry run and safe to build on.
The money flows are not blocked in the same way, and the difference matters if
you are writing a client:
Converting between your own accounts is refused outright.
settle_fx_quote checks the session's declared card support before anything
else and fails closed, so a cardless session cannot convert currency however
it calls the tool. create_fx_quote does the same. Send those users to the
Sokin portal.
Paying a beneficiary works fine without a card, and is meant to. The
prepare step returns the figures, the idempotency key and (for an FX payment)
the quote id in the text, and asks the assistant to read them back and get
your explicit approval in the chat before submitting. So a plain-chat
prepare → confirm → submit flow is the supported path on those clients, not
a workaround.
Where the human control actually sits for a payment. Not in the card, and
not in a capability check — the server performs none for either payment pair.
It sits in two places: your client's own confirmation prompt before a
destructive tool, and the company's own approval rules in the Sokin B2B
portal, which route every MCP-originated payment to a human sign-off there.
Both are load-bearing rather than decorative.
Checking your client: if create_fx_quote and settle_fx_quote are absent
from its tool list, it definitely did not declare card support. Their presence
does not prove the opposite — the server hides them only from a session it
knows lacks support, and shows everything when it cannot tell, so an
uninitialised client can see them and still be refused. The reliable signal is
whether a card actually renders.

Moving money#

Seven tools change something, and three of them move real money. Those
three are the ones this section is about; the other four — create_fx_quote,
validate_beneficiary, create_beneficiary and update_beneficiary — write
something without moving a penny. None of the three that do move money is a single call you can
stumble into: each is the second half of a pair, and will only ever submit
exactly what the first half prepared.
ToolWhat it does
create_fx_quoteCreates a real, short-lived exchange rate. Moves no money
settle_fx_quoteConverts real money between two of your own accounts
submit_payment_instructionSends a real payment to a beneficiary
submit_fx_payment_instructionSends a real payment, converting as it goes
Each is the second half of a pair: one tool prepares and shows you what
will happen, and the second submits it. The second will only ever send exactly
what was prepared — not a changed version, and not twice, permanently.
Which pair you want depends on the currency:
You want toPair
Pay someone in a currency you holdcreate_payment_instruction → submit_payment_instruction
Pay someone in a currency you do not holdcreate_fx_payment_instruction → submit_fx_payment_instruction
Convert between two of your own accountsget_fx_quote card → create_fx_quote → settle_fx_quote
What the pair does not do is prove a human approved. The key that
authorises the second call reaches the assistant either way — through the card
on clients that render one, and spelled out in the text on clients that do
not — because MCP gives a card no private channel. For converting between your
own accounts the server closes that gap by refusing outright without a card.
For paying a beneficiary it deliberately does not, because the real control
there is the company's approval rules in the Sokin portal — which route
every MCP-originated payment to a human sign-off there — plus your client's
own confirmation prompt.
Sending a payment prepares in the conversation and commits from a review card:
Converting currency happens entirely inside one card, because an exchange rate
expires too quickly to survive a back-and-forth in chat:
Submitting is not the same as finished. Both flows hand back a receipt
reference and do the work afterwards. get_instruction_request tells you how
it actually ended.

The tools, one by one#

Each section below gives the tool's name, what it does, what it takes, one
example of what your client sends, and one example of what comes back.
Where a section says "see the Technical Docs", it means the Sokin MCP —
Technical Docs
page, which carries the exact response schemas field by
field, every possible value of every coded field, and the error and pagination
contracts. This page is the guide; that one is the specification.

list_corporates#

Lists the companies your login can act for. Usually the first call of any
session
— nearly every other tool needs a corporate reference, and this is
where one comes from. Read-only.
Takes: nothing.
Request
{"name": "list_corporates", "arguments": {}}
Reply
2 corporates: CORP-8842 — Northwind Trading Ltd (direct assignment); CORP-9110 — Northwind Logistics BV (inherited access).
Reply as data
{
  "data": {
    "items": [
      {"corporateReference": "CORP-8842", "corporateName": "Northwind Trading Ltd", "assignmentType": "direct"},
      {"corporateReference": "CORP-9110", "corporateName": "Northwind Logistics BV", "assignmentType": "inherited"}
    ]
  }
}
assignmentType is direct if you were given access to that company, or
inherited if it comes via a parent.

list_accounts#

All of a company's currency accounts, with balances. Read-only.
TakesRequiredWhat it is
corporate_referenceyesFrom list_corporates
Request
{"name": "list_accounts", "arguments": {"corporate_reference": "CORP-8842"}}
Reply
2 accounts for CORP-8842: CCA-1001 (GBP) — balance 48200.00, of which 47150.00 available; CCA-1002 (USD) — balance 12000.00, available balance not computable.
Reply as data
{
  "message": "Success.",
  "data": [
    {
      "corporateReference": "CORP-8842",
      "corporateCurrencyAccountReference": "CCA-1001",
      "currencyCode": "GBP",
      "balance": "48200.00",
      "availableBalance": "47150.00"
    },
    {
      "corporateReference": "CORP-8842",
      "corporateCurrencyAccountReference": "CCA-1002",
      "currencyCode": "USD",
      "balance": "12000.00",
      "availableBalance": null
    }
  ],
  "pagination": {"nextToken": null, "previousToken": null, "totalItems": 2, "hasMore": false}
}
Balance and available balance are different numbers. Available is the
balance minus whatever is reserved by payments already in flight. It can come
back empty when your login lacks permission to see instructions — the sentence
says so in words rather than showing a zero, and code should expect null.

get_account#

Everything about one account, including the bank details someone would use to
pay money into it. Read-only.
TakesRequiredWhat it is
account_referenceyesAn account reference from list_accounts
Request
{"name": "get_account", "arguments": {"account_reference": "CCA-1001"}}
Reply
Account CCA-1001 (GBP): balance 48200.00, of which 47150.00 available. Local funding: active — ready to receive payments — pay into Northwind Trading Ltd, account 12345678, SortCode 04-00-75, at Modulr FS Limited (London branch), GB. International funding: not yet set up.
Reply as data
{
  "message": "Success.",
  "data": {
    "externalReference": "CCA-1001",
    "currency": "GBP",
    "currentBalance": "48200.00",
    "availableBalance": "47150.00",
    "localAccountStatus": "ACTIVE",
    "internationalAccountStatus": "NOT_PROVISIONED",
    "localPaymentDetails": {
      "accountName": "Northwind Trading Ltd",
      "accountNumber": "12345678",
      "routingCodeType": "SortCode",
      "routingCode": "04-00-75",
      "bankName": "Modulr FS Limited",
      "bankBranch": "London",
      "bankCountry": "GB"
    },
    "internationalPaymentDetails": []
  }
}
An account can have a local route, international routes, or neither set up
yet, so expect either block to be empty. The payment-details objects also
carry IBAN, BIC and address fields where they apply — see the Technical Docs
for the full list.

get_account_ledger#

Transaction history for one account, newest first, a page at a time.
Read-only.
TakesRequiredWhat it is
account_referenceyesThe account to look at
from_datenoOnly entries from this date (YYYY-MM-DD)
to_datenoOnly entries up to this date
limitnoEntries per page (default 25, max 100)
next_tokennoAsk for the next page, using the token from the last one
Request
{
  "name": "get_account_ledger",
  "arguments": {"account_reference": "CCA-1001", "from_date": "2026-08-01", "limit": 25}
}
Reply
3 ledger entries for CCA-1001 (amounts shown as +credit/-debit):
DateTypeAmountCounterpartyDescription
2026-08-14deposit+5000.00Contoso LtdInvoice 4471
2026-08-15withdrawal-1250.00Fabrikam GmbHSupplier payment
2026-08-16FX Trade-800.00GBP to EUR conversion
More results available -- pass next_token='eyJrIjoi...' to see more.
Reply as data
{
  "message": "Success.",
  "data": [
    {
      "amount": "5000.00",
      "direction": "credit",
      "type": "deposit",
      "effectiveDate": "2026-08-14",
      "sourceReference": "Contoso Ltd",
      "beneficiary": null,
      "description": "Invoice 4471",
      "transactionReference": "TXN-77120"
    },
    {
      "amount": "1250.00",
      "direction": "debit",
      "type": "withdrawal",
      "effectiveDate": "2026-08-15",
      "sourceReference": null,
      "beneficiary": "Fabrikam GmbH",
      "description": "Supplier payment",
      "transactionReference": "TXN-77121"
    },
    {
      "amount": "800.00",
      "direction": "debit",
      "type": "FX Trade",
      "effectiveDate": "2026-08-16",
      "sourceReference": null,
      "beneficiary": null,
      "description": "GBP to EUR conversion",
      "transactionReference": "TXN-77122"
    }
  ],
  "pagination": {"nextToken": "eyJrIjoi...", "previousToken": null, "totalItems": 3, "hasMore": true}
}
The amount has no sign — money in and money out both come back positive,
and direction says which it is. The +/- in the table above is added for
readability. The counterparty sits in sourceReference for money in and
beneficiary for money out.
type covers deposits, withdrawals, FX trades and more, and not every value
has a friendly label — see the Technical Docs for the full set before writing
code that switches on it.

list_beneficiaries#

Who a company can pay. Mostly used to check whether a payee already exists,
and to get the reference a payment needs. Read-only, a page at a time.
You can create and change a payee, but not delete one.
create_beneficiary adds one and update_beneficiary changes its bank or
identity details, both behind an emailed confirmation code. Nothing removes a
payee. get_beneficiary_schema tells you what a new payee needs, and
validate_beneficiary checks details before you commit to them.
TakesRequiredWhat it is
corporate_referenceno, but effectively yesThe company whose payees you want
searchnoMatch against the name. Leave out to list everyone
search_modeno"contains" (default) or "starts_with"
limitnoPer page
next_tokennoToken from the previous page
Request
{
  "name": "list_beneficiaries",
  "arguments": {"corporate_reference": "CORP-8842", "search": "fab", "search_mode": "contains"}
}
Reply
2 beneficiaries:
ReferenceNameTypeCountryCurrencies (rails)Status
BEN-3301Fabrikam GmbHcompanyDEEUR (sepa/swift)active
BEN-3302Jane OkaforindividualGBGBP (faster_payments)active
Reply as data
{
  "data": [
    {
      "externalReference": "BEN-3301",
      "displayName": "Fabrikam GmbH",
      "entityType": "company",
      "countryCode": "DE",
      "destinations": [
        {"currencyCode": "EUR", "paymentRail": "sepa"},
        {"currencyCode": "EUR", "paymentRail": "swift"}
      ],
      "status": "active"
    },
    {
      "externalReference": "BEN-3302",
      "displayName": "Jane Okafor",
      "entityType": "individual",
      "countryCode": "GB",
      "destinations": [{"currencyCode": "GBP", "paymentRail": "faster_payments"}],
      "status": "active"
    }
  ],
  "pagination": {"nextToken": null, "previousToken": null, "totalItems": 2, "hasMore": false}
}
A payee can be reachable in the same currency by more than one route — the
example above can take euros over SEPA or SWIFT, which differ in speed and
cost — so each currency-and-route pair is listed separately rather than
collapsed into one.
This tool also accepts page and offset, purely so it can tell you clearly
that they are not supported. Use limit and next_token.

get_beneficiary#

One payee in full, by reference. Where list_beneficiaries collapses a payee
to its currency-and-route pairs, this returns each destination's actual
account details
— account numbers, routing data, an e-Transfer email, a
crypto wallet address — plus how far each one has been verified. Read-only.
TakesRequiredWhat it is
beneficiary_idyesThe payee's reference from list_beneficiaries
corporate_referenceno, but effectively yesThe company the payee belongs to
Request
{"name": "get_beneficiary", "arguments": {"beneficiary_id": "BEN-3301", "corporate_reference": "CORP-8842"}}
Reply
Fabrikam GmbH -- company, DE, active
CurrencyRailValidationAccount
EURsepamatchedDE89370400440532013000
EURswiftnot_validatedDE89370400440532013000
Created 2026-02-11T09:20:00Z.
Reply as data
{
  "data": {
    "externalReference": "BEN-3301",
    "displayName": "Fabrikam GmbH",
    "entityType": "company",
    "countryCode": "DE",
    "status": "active",
    "isInternal": false,
    "destinations": [
      {
        "currencyCode": "EUR",
        "paymentRail": "sepa",
        "validationStatus": "matched",
        "account_number": "DE89370400440532013000",
        "institution": "Commerzbank AG"
      },
      {
        "currencyCode": "EUR",
        "paymentRail": "swift",
        "validationStatus": "not_validated",
        "account_number": "DE89370400440532013000",
        "institution": "Commerzbank AG"
      }
    ],
    "company": {"registeredName": "Fabrikam GmbH"},
    "address": {"city": "Berlin", "countryCode": "DE"},
    "createdAt": "2026-02-11T09:20:00Z"
  }
}
The Validation column is worth reading before you pay someone.
matched means the account details were checked against the account holder's
name; not_validated means they were not. It is per destination, so the same
payee can be verified on one route and unverified on another.
A payee whose status is deleted gets an explicit closing line saying it
cannot receive payments — do not infer that from the status alone.
One thing this never returns: for an e-Transfer destination without
auto-deposit, the security question comes back but the answer never does.
That answer is what the recipient has to give to claim the money.

get_beneficiary_schema#

What details a new payee needs, for a given payment method, currency and
country — field names, types, which are required, and what values are allowed.
Use it to collect the right information before creating a payee, rather than
guessing and being rejected. Read-only.
Payment method here is a coarser grouping than the payment rails you
see elsewhere. Every rail belongs to exactly one method:
MethodRails
bank_accountfaster_payments, sepa, swift, ach, eft, and the rest
e_transfere_transfer
virtual_accountpix
sokin_internalinternal
cryptobase, tron, ethereum, polygon, solana
TakesRequiredWhat it is
payment_methodyesOne of the five above. crypto is always refused — the upstream endpoint cannot describe crypto payees yet
currency_codeunless sokin_internalCurrency the payee is paid in
country_codeunless sokin_internalCountry of the payee's account
entity_typeunless sokin_internalindividual or company
autodeposit_enrollednoe-Transfer only: "true", "false" or "unknown". Pass "unknown" rather than omitting it when you have not checked — omitting behaves like "true" and leaves the security question optional
corporate_referenceno, but effectively yesThe company the payee will belong to
Request
{
  "name": "get_beneficiary_schema",
  "arguments": {
    "payment_method": "bank_account",
    "currency_code": "GBP",
    "country_code": "GB",
    "entity_type": "company",
    "corporate_reference": "CORP-8842"
  }
}
Reply
Beneficiary schema for bank_account / GBP / GB / company -- 4 fields:
FieldTypeRequiredNotes
displayNamestringyeslength 1-100
bankAccount.accountNumberstringonly for faster_payments
bankAccount.sortCodestringyes
entityTypeenumyesone of: individual, company
Reply as data
{
  "data": {
    "condition": {
      "currencyCode": "GBP",
      "entityType": "company",
      "countryCode": "GB",
      "paymentMethod": "bank_account"
    },
    "fields": [
      {"name": "displayName", "fieldType": "string", "isRequired": true, "min": 1, "max": 100},
      {
        "name": "bankAccount.accountNumber",
        "fieldType": "string",
        "isRequired": true,
        "requiredForPaymentRails": ["faster_payments"]
      },
      {"name": "bankAccount.sortCode", "fieldType": "string", "isRequired": true},
      {
        "name": "entityType",
        "fieldType": "enum",
        "isRequired": true,
        "allowedValues": ["individual", "company"]
      }
    ]
  }
}
Field names are dot-paths into the payee payload, so address.city and
bankAccount.accountNumber tell you where each answer belongs. "Only for
rails" means required just for those routes; "conditional on X" means it
only applies once X has been answered.
A combination the company has no route for is rejected upstream, e.g.
API error (400): No payment rails for GBP+US and bank_account.

validate_beneficiary#

Checks payee details before you rely on them — that an account number
really belongs to the name you were given, that an e-Transfer email can
receive money, or that an existing payee's details still check out.
Exactly one check per call, and which fields you pass decides which:
To checkPass
An existing payee, all destinationsbeneficiary_id
Whether an e-Transfer email can auto-depositemail
An IBAN against a nameiban + bic + account_name
A UK account against a namesort_code + account_number + account_name
TakesRequiredWhat it is
beneficiary_idfor the first checkThe payee's reference from list_beneficiaries
emailfor the secondRecipient email to probe
ibanfor the thirdThe IBAN, 15–34 characters
bicfor the thirdBIC/SWIFT, 8 or 11 characters
account_namethird and fourthThe account holder name to verify
sort_codefor the fourthUK sort code, 6 digits. Spaces and hyphens are fine
account_numberfor the fourthUK account number, 8 digits
corporate_referenceno, but effectively yesThe company to check under
Request
{
  "name": "validate_beneficiary",
  "arguments": {
    "sort_code": "04-00-75",
    "account_number": "12345678",
    "account_name": "Jane Okafor",
    "corporate_reference": "CORP-8842"
  }
}
Reply
Account name verified.
CurrencyRailCheckStatusName on recordDetails
GBPfaster_paymentsCOPmatchedJane OkaforName matches the account holder
Reply as data
{
  "data": {
    "isValid": true,
    "results": [
      {
        "validationType": "COP",
        "status": "matched",
        "actualName": "Jane Okafor",
        "details": "Name matches the account holder",
        "currencyCode": "GBP",
        "paymentRail": "faster_payments"
      }
    ]
  }
}
Read the first line, not just the table. It reports the worst result, so
a payee with one verified destination and one unverifiable one never reads as
though everything passed. Three outcomes are easy to confuse:
StatusWhat it means
matchedThe name checks out
close_matchNearly — show the user both names and ask before going further
no_match, invalidNot verified
check_not_possible, not_validatedThe check did not run. Not a pass
Two things it cannot tell you. An e-Transfer result confirms auto-deposit
enrolment, not the account name
— the reply says so when that applies. And
not every corridor supports a name check: internal and non-euro bank
destinations come back not_validated, meaning "not applicable" rather than
"failed".
This is the only read-shaped tool here that writes. Checking an existing
payee saves each refreshed status back, which is why it is not marked
read-only. It moves no money, and re-running it produces the same answer.

create_beneficiary#

Creates a new payee, behind a one-time code emailed to you. This is one
tool you call twice.
1.
Call it with the payee's details. The draft is checked first, so a bad
draft costs nothing and sends no email. If your company requires a code,
one is emailed and the reply comes back otp_required.
2.
Call it again with exactly the same arguments plus otp_code.
If your company does not require a code, the first call creates the payee.
TakesRequiredWhat it is
display_nameyesPayee name, 1–255 characters
currency_codeyesISO 4217, or the stablecoin code for a crypto rail
payment_railyese.g. faster_payments, sepa, swift, eft, e_transfer, internal, pix
entity_typeunless rail internalindividual or company
country_codeunless rail internalCountry of the payee's account
one destination objectyesbank_account, e_transfer, virtual_account, sokin_internal or crypto — whichever matches the rail. get_beneficiary_schema tells you the fields
individual / companyper entity_typeIdentity details
addressbank rails and e_transferNot needed for internal, pix or crypto
corporate_referenceno, but effectively yesThe company to create under
otp_codeon the second callThe 6-character code from your email
Request
{
  "name": "create_beneficiary",
  "arguments": {
    "display_name": "Contoso Ltd",
    "currency_code": "GBP",
    "payment_rail": "faster_payments",
    "entity_type": "company",
    "country_code": "GB",
    "bank_account": {"accountNumber": "12345678", "sortCode": "040075"},
    "company": {"registeredName": "Contoso Ltd"},
    "address": {
      "addressLineOne": "1 High St",
      "city": "London",
      "postCode": "EC1A 1AA",
      "countryCode": "GB"
    },
    "corporate_reference": "CORP-8842"
  }
}
Reply
Payee created: Contoso Ltd (reference BEN-3310, status active).
Destinations: GBP via faster_payments (name verified).
No confirmation code was required for this corporate.
Reply as data
{
  "data": {
    "externalReference": "BEN-3310",
    "displayName": "Contoso Ltd",
    "status": "active",
    "createdAt": "2026-09-17T09:14:00Z",
    "destinations": [
      {
        "currencyCode": "GBP",
        "paymentRail": "faster_payments",
        "validationStatus": "matched"
      }
    ]
  }
}
Check list_beneficiaries first. There is no duplicate detection: call
this twice and you get two payees. The only thing stopping a genuine
double-submit is a short in-flight lock while one create is in progress.
About the code, because getting it wrong has consequences beyond this
tool:
It is 6 characters, single-use, and expires after 10 minutes.
Only the most recent email works. Each new code replaces the last one,
across drafts — so if you triggered two, only the newer code is live.
Five wrong attempts temporarily locks the account, including the Sokin
portal login.
Never guess a code or retry one; ask the user to read the
latest email.
The second call must repeat the first call's arguments byte for byte,
corporate_reference included. Change anything and the flow restarts with a
fresh code. That includes confirm_verified_payee_name, which you would only
pass after a name mismatch the user explicitly accepted.

update_beneficiary#

Changes an existing payee's bank or identity details, behind the same
emailed one-time code as create_beneficiary. Called twice, exactly like
creating one: once to trigger the code, once with the code to apply the change.
Send only what changes. Anything you leave out stays as it was — and a
field cannot be emptied, only given a new value.
Four things cannot be changed at all: the payee's name, whether it is a
person or a company, its country, and its address. Those are fixed once the
payee exists; changing any of them means creating a new payee. The tool refuses
them straight away rather than letting you burn a code first.
TakesRequiredWhat it is
beneficiary_idyesThe payee to change, from list_beneficiaries
currency_code + payment_railif the payee has more than one destinationWhich destination you mean, not a change to it. Supply both together
bank_account, e_transfer, virtual_account, sokin_internal, crypto, individual, companyat least oneThe fields to change
corporate_referenceno, but effectively yesThe company the payee belongs to
otp_codeon the second callThe 6-character code from your email
Request
{
  "name": "update_beneficiary",
  "arguments": {
    "beneficiary_id": "BEN-3310",
    "bank_account": {"accountNumber": "87654321"},
    "corporate_reference": "CORP-8842",
    "otp_code": "A1B2C3"
  }
}
The same call without otp_code is what triggers the email:
A 6-character confirmation code has been emailed to the user. Ask them for it, then call update_beneficiary again with the SAME arguments plus otp_code. The code is single-use and expires in 10 minutes; to get a fresh one, call again without otp_code (max 5 emails per 15 minutes).
Reply
Payee updated.
Contoso Ltd -- company, GB, active
CurrencyRailValidationAccount
GBPfaster_paymentsmatched87654321
Created 2026-09-17T09:14:00Z.
Reply as data
{
  "data": {
    "externalReference": "BEN-3310",
    "displayName": "Contoso Ltd",
    "entityType": "company",
    "countryCode": "GB",
    "status": "active",
    "createdAt": "2026-09-17T09:14:00Z",
    "destinations": [
      {
        "currencyCode": "GBP",
        "paymentRail": "faster_payments",
        "validationStatus": "matched",
        "account_number": "87654321"
      }
    ]
  }
}
A selector can add rather than change. If the currency and rail you name
are ones the payee does not have yet, and you send destination fields with
them, you add a new way of paying them instead of editing an existing one.
Both are legitimate, so be sure which the user meant before calling.
Two behaviours that surprise people:
A validation error can name fields you never sent. Sokin merges your
change into the stored payee and re-checks the whole result, so an error may
point at something that was already there.
An unknown payee fails before any code is used. Wrong beneficiary_id,
or the right one under the wrong company, both read as "not found" — and no
code is emailed or consumed, so nothing is wasted.
The code rules are the same as create_beneficiary's, including that five
wrong attempts temporarily locks the account
. Never guess or retry a code.

list_financial_institutions#

Reference data: banks available when setting up a payee, for the routes that
need one (Canadian EFT, for instance). Read-only, not filterable — the
underlying service takes no options, so neither does this.
Takes: nothing.
Request
{"name": "list_financial_institutions", "arguments": {}}
Reply
2 financial institutions:
IdNameBank Number
1Royal Bank of Canada003
2Toronto-Dominion Bank004
Reply as data
{
  "message": "Success.",
  "data": [
    {"id": 1, "name": "Royal Bank of Canada", "bankNumber": "003"},
    {"id": 2, "name": "Toronto-Dominion Bank", "bankNumber": "004"}
  ]
}
Do not treat this as a complete list of valid banks. It is passed through
from a third party that silently drops records it cannot validate, so a real,
usable bank can be missing from it.

list_financial_institution_branches#

A bank's branches, with routing numbers and addresses, for completing a
payee's details. Read-only, a page at a time.
TakesRequiredWhat it is
financial_institution_idyesThe id from list_financial_institutions
limitnoPer page
next_tokennoToken from the previous page
Request
{"name": "list_financial_institution_branches", "arguments": {"financial_institution_id": 1}}
Reply
1 branch:
IdDescriptionRouting NumberAddressCityStatePostal Code
4412Main Branch000312345200 Bay Street, Suite 400TorontoONM5J2J2
Reply as data
{
  "data": [
    {
      "id": 4412,
      "description": "Main Branch",
      "routingNumber": "000312345",
      "addressLineOne": "200 Bay Street, Suite 400",
      "addressLineTwo": null,
      "city": "Toronto",
      "state": "ON",
      "postalCode": "M5J2J2"
    }
  ],
  "pagination": {"nextToken": null, "previousToken": null, "totalItems": 1, "hasMore": false}
}
An empty result can mean either an unrecognised bank id or a bank with no
branches listed — the service gives no way to tell those apart, so the tool
suggests checking the id against list_financial_institutions.

list_instructions#

Payments and conversions for a company, or for one account. Read-only, a page
at a time.
TakesRequiredWhat it is
corporate_referenceone of these twoList for a whole company
cca_referenceone of these twoList for a single account
completednotrue for finished, false for still in progress
limitnoPer page (default 20, max 100)
next_tokennoToken from the previous page
Give exactly one of the first two — neither or both is an error.
Request
{"name": "list_instructions", "arguments": {"corporate_reference": "CORP-8842", "limit": 20}}
Reply
2 instructions for CORP-8842:
ReferenceAccountTypeAmountCurrencyStatusCreated
INS-5512CCA-1001Payment1250.00GBPProcessed2026-08-15T09:12:44Z
INS-5513CCA-1002FXPayment800.00USDPending Settlement2026-08-16T11:03:02Z
Reply as data
{
  "message": "Success.",
  "data": [
    {
      "instructionReference": "INS-5512",
      "corporateReference": "CORP-8842",
      "ccaReference": "CCA-1001",
      "instructionType": "Payment",
      "amount": "1250.00",
      "currency": "GBP",
      "displayStatus": "Processed",
      "createdAt": "2026-08-15T09:12:44Z",
      "updatedAt": "2026-08-15T09:14:02Z"
    },
    {
      "instructionReference": "INS-5513",
      "corporateReference": "CORP-8842",
      "ccaReference": "CCA-1002",
      "instructionType": "FXPayment",
      "amount": "800.00",
      "currency": "USD",
      "displayStatus": "Pending Settlement",
      "createdAt": "2026-08-16T11:03:02Z",
      "updatedAt": "2026-08-16T11:04:18Z"
    }
  ],
  "pagination": {"nextToken": null, "previousToken": null, "totalItems": 2, "hasMore": false}
}
The account reference matters: a company can hold several accounts in the
same currency, so the currency alone does not tell you which one the money
came from.

get_instruction#

Everything about one payment or conversion — status, amounts, fees, payee.
Read-only.
TakesRequiredWhat it is
instruction_referenceyesThe instruction to look up
Request
{"name": "get_instruction", "arguments": {"instruction_reference": "INS-5513"}}
Reply
Instruction INS-5513 (FXPayment) for corporate CORP-8842, account CCA-1002: 800.00 USD -> 742.10 EUR, status Pending Settlement. Beneficiary: BEN-3301. Sender reference (shown to the recipient): INV-4471. Purpose: SUPPLIER_PAYMENT. Fees: 4.20 FX, 1.50 transaction, 0.00 reseller. Created 2026-08-16T11:03:02Z, updated 2026-08-16T11:04:18Z.
Reply as data
{
  "message": "Success.",
  "data": {
    "externalReference": "INS-5513",
    "displayStatus": "Pending Settlement",
    "instructionType": "FXPayment",
    "corporateReference": "CORP-8842",
    "corporateCurrencyAccount": "CCA-1002",
    "amount": "800.00",
    "destinationAmount": "742.10",
    "sourceCurrency": "USD",
    "destinationCurrency": "EUR",
    "beneficiaryReference": "BEN-3301",
    "senderReference": "INV-4471",
    "paymentPurpose": "SUPPLIER_PAYMENT",
    "sokinFxFee": "4.20",
    "sokinTransactionFee": "1.50",
    "resellerTransactionFee": "0.00",
    "failureReason": null,
    "instructionRequestReference": "IR-Payment-5513",
    "createdAt": "2026-08-16T11:03:02Z",
    "updatedAt": "2026-08-16T11:04:18Z"
  }
}
Parts of the sentence appear only when they apply — a destination amount only
when a conversion happened, a failure reason only when something failed. The
full field list, including a few internal ones this trims away, is in the
Technical Docs.

get_instruction_request#

How a payment or conversion actually turned out. When you submit one, it
is accepted for processing first and worked on afterwards, so being accepted
does not mean it went through. This is the tool that tells you.
Read-only.
TakesRequiredWhat it is
instruction_request_referenceyesThe receipt reference from submitting. Not an instruction reference — those go to get_instruction
Request
{"name": "get_instruction_request", "arguments": {"instruction_request_reference": "IR-Payment-77"}}
Reply
Instruction request IR-Payment-77 was accepted: instruction INS-9 was created. Use get_instruction for its full detail.
Reply as data
{
  "message": "Success.",
  "data": {
    "externalReference": "IR-Payment-77",
    "status": "Accepted",
    "createdInstructionReference": "INS-9",
    "failureReason": null,
    "errorCategory": null
  }
}
Read the status carefully, because the words are misleading:
StatusWhat it actually means
CreatedStill being processed. Not a success
AcceptedSucceeded
RejectedFailed, and the reason is in the result
The other two outcomes read like this:
Instruction request IR-Payment-77 is still being processed -- no outcome yet. Check again shortly.
Instruction request IR-Payment-77 was rejected: Insufficient balance (category: amount). No instruction was created.
If a reference comes back as not found, it is almost always the wrong
reference — commonly an instruction reference used here by mistake. The one
exception is a submission made seconds ago, which can briefly read as missing
while it is being saved; check once more, then stop.

validate_payment_instruction#

Checks a payment without sending it. Runs the same checks as the real
thing — account, payee, balance, fees — and tells you whether it would work.
Nothing is saved and no money moves.
This is the tool to use before preparing a payment, and the one payment tool
that is safe to build on in a client without interactive cards.
TakesRequiredWhat it is
corporate_currency_accountyesThe account to pay from
beneficiary_referenceyesWho to pay, from list_beneficiaries
amountyesPlain decimal, up to 2 places, above zero (e.g. "125.50")
reseller_feenoSame format, zero or more
sender_referencenoText the recipient sees, max 18 characters
payment_purposenoOne of a fixed list of purpose codes, e.g. ACCOUNTS_PAYABLE. Any other value is refused
amount_is_fees_inclusivenotrue takes fees out of the amount; default adds them on top
Request
{
  "name": "validate_payment_instruction",
  "arguments": {
    "corporate_currency_account": "CCA-1001",
    "beneficiary_reference": "BEN-3301",
    "amount": "1250.00",
    "reseller_fee": "1.50",
    "sender_reference": "INV-4471"
  }
}
Reply
Validation passed: this payment would clear the creation checks. Total debit 1256.70 (5.20 Sokin fee, 1.50 reseller fee); beneficiary would receive 1250.00. Nothing was created and no money moved.
Reply as data
{
  "data": {
    "success": true,
    "failureReason": null,
    "errorCategory": null,
    "feeBreakdown": {
      "amount": "1256.70",
      "destinationAmount": "1250.00",
      "sokinFee": "5.20",
      "resellerFee": "1.50",
      "feeRateCardReference": "RC-14"
    }
  }
}
You get the costs even when it fails, which is what makes this useful for
checking affordability:
Validation failed: insufficient available balance on the source account (category: amount). The engine still computed amounts: total debit 1256.70 (5.20 Sokin fee, 1.50 reseller fee). Nothing was created and no money moved.
The amount has to be a plain number like "125.50". Shorthand such as 1e3
is rejected rather than interpreted — for anything to do with money, quietly
guessing what you meant is the worst thing the tool could do.

create_payment_instruction#

Prepares a payment for you to look at. It does not send it. Despite the
name, no payment exists after this and no money has moved.
What it does: re-runs the checks so the card shows Sokin's own figures rather
than the assistant's, then shows you a review card with Approve and
Cancel
. Only your Approve click sends anything.
Needs a client that renders interactive cards. Without one it still
prepares a draft, but no card appears, so nothing can approve it and no
payment can be made. It fails safely — but quietly.
Run validate_payment_instruction first and fix anything it flags. This
tool checks again internally as a guard, and a draft that fails produces no
card at all.
Takes the same arguments as validate_payment_instruction, plus one optional
status_check_key that only the card itself sends. Leave it unset.
Request
{
  "name": "create_payment_instruction",
  "arguments": {
    "corporate_currency_account": "CCA-1001",
    "beneficiary_reference": "BEN-3301",
    "amount": "1250.00",
    "reseller_fee": "1.50",
    "sender_reference": "INV-4471"
  }
}
Reply
Payment prepared and validated -- awaiting the user's confirmation in the review widget. Nothing has been submitted; do not report this payment as made, and do not call submit_payment_instruction yourself.
Reply as data
{
  "draft": {
    "corporate_currency_account": "CCA-1001",
    "beneficiary_reference": "BEN-3301",
    "amount": "1250.00",
    "reseller_fee": "1.50",
    "sender_reference": "INV-4471",
    "payment_purpose": null,
    "amount_is_fees_inclusive": false
  },
  "currency": "GBP",
  "validation": {
    "success": true,
    "failureReason": null,
    "errorCategory": null,
    "feeBreakdown": {
      "amount": "1256.70",
      "destinationAmount": "1250.00",
      "sokinFee": "5.20",
      "resellerFee": "1.50",
      "feeRateCardReference": "RC-14"
    }
  },
  "idempotencyKey": "a5f1c8e0-7d3b-4f2a-9c61-0b8e4d2f1a77"
}
That key is how the server recognises this exact draft when you approve it.
You never need to handle it yourself.
If you have already sent this exact payment, the result says so and the
card appears with no Approve button — the same payment cannot be sent
twice. To pay the same person the same amount again on purpose, give it a
different sender reference, which makes it a different payment. There is no
way to override this, by design.
A prepared payment you never approve expires after 24 hours, and nothing
happens to it in the meantime.
Coming back to the conversation later. When you reopen a chat, the card
shows the payment's details straight away, but offers nothing until it has
checked with Sokin: where the Approve button goes, it says it is checking.
A payment you already approved
shows as Already submitted with its reference and no Approve button, and
the card then shows where it stands now: waiting for approval, completed,
rejected or failed. If a payment with the same details is still being
submitted, or Sokin never confirmed whether it went through, the card says it
may already have gone out and offers only Check again. An expired one says
so. If the check cannot reach Sokin, or gets no answer within about 15
seconds, the card says it could not check and offers to try again; it never
offers Approve without an answer.

submit_payment_instruction#

Sends the payment. This moves real money to someone else. The real
control is approval in your company's Sokin portal, not this tool — every
company gets an approval rule routing MCP-originated payments there. It
cannot be cancelled once sent.
Your Approve click on the review card runs this. It is not meant to be
called directly
— it only accepts a payment that was prepared and shown to
you, unchanged, and only once.
TakesRequiredWhat it is
idempotency_keyyesThe key from create_payment_instruction
everything else—Must match the payment you reviewed, exactly
Request
{
  "name": "submit_payment_instruction",
  "arguments": {
    "idempotency_key": "a5f1c8e0-7d3b-4f2a-9c61-0b8e4d2f1a77",
    "corporate_currency_account": "CCA-1001",
    "beneficiary_reference": "BEN-3301",
    "amount": "1250.00",
    "reseller_fee": "1.50",
    "sender_reference": "INV-4471"
  }
}
Reply
Payment submitted: instruction request IR-Payment-77 for 1250.00 from CCA-1001 to BEN-3301. This is an instruction-request reference, not a completed payment -- it now needs human approval in the Sokin portal before it is processed. Check the outcome via get_instruction_request.
Reply as data
{"data": {"externalReference": "IR-Payment-77"}}
That receipt is not confirmation the payment succeeded. Pass the reference
to get_instruction_request for the real outcome.
What the server refuses, without sending anything:
A payment that was not prepared and shown to you first.
Any change to the amount, payee or reference after you saw it.
The same payment a second time — permanently, not just for a while.
Two approvals racing each other; exactly one can win.
Approving the same payment twice — a double click, a retry — sends nothing
again. The tool looks up what happened the first time and tells you.
If you are building your own client rather than using Claude: the review card
cannot prove who clicked Approve, so your client's own confirmation
prompt for this tool is doing real work.
Treat it as required, not
decorative. The Technical Docs explain the guarantees and their limits
precisely.

create_fx_payment_instruction#

Pays a beneficiary in a currency you do not hold, converting and paying in
one motion. Prepares it for review — it submits nothing, and despite the
name no payment exists after this call.
Use it when the payee wants one currency and you are spending another. For a
payment in a currency you already hold, use create_payment_instruction
instead; for converting between two of your own accounts with no payee, use
the FX quote card.
You do not fetch a rate first. This tool mints its own live quote
internally from the currencies and amount you give it, then runs the same
dry run as a normal payment — so a problem with the payee (missing, cannot
receive that currency, fails verification) is reported here rather than after
someone clicks Approve.
TakesRequiredWhat it is
corporate_currency_accountyesThe account to pay from
beneficiary_referenceyesWho to pay, from list_beneficiaries
buy_currencyyesWhat the beneficiary receives, e.g. "USD"
sell_currencyyesWhat you are spending, e.g. "GBP"
amountyesThe amount of whichever side fixed_side names
fixed_sideno"BUY" or "SELL". Default "SELL"
corporate_referenceno, but effectively yesThe company to quote for. Used only to mint the quote
reseller_feenoPlain decimal, zero or more
sender_referencenoText the recipient sees, max 18 characters
payment_purposenoOne of a fixed list of purpose codes, e.g. ACCOUNTS_PAYABLE. Any other value is refused
status_check_keynoSent only by the card itself, to check a draft's status. Leave it unset
Request
{
  "name": "create_fx_payment_instruction",
  "arguments": {
    "corporate_currency_account": "CCA-1001",
    "beneficiary_reference": "BEN-3301",
    "buy_currency": "USD",
    "sell_currency": "GBP",
    "amount": "100.00",
    "fixed_side": "SELL",
    "corporate_reference": "CORP-8842",
    "sender_reference": "INV-4471"
  }
}
Reply, on a client with cards — a not-submitted notice:
FX payment prepared and validated -- awaiting the user's confirmation in the review widget. Nothing has been submitted; do not report this payment as made, and do not call submit_fx_payment_instruction yourself.
Reply, on a client without cards — the figures and both references are
spelled out in the text, because there is no card to carry them:
FX payment prepared and validated -- nothing has been submitted yet. Total debit 100.00 (0.50 Sokin fee, 0.00 reseller fee); beneficiary would receive 125.00 (GBP debited, beneficiary receives USD). idempotency key: 9c21ab4f-... . quote_id: FXQ-1a2b3c... . Read these figures back to the user and get their explicit approval. Only after they approve, call submit_fx_payment_instruction with this idempotency_key, this quote_id, and the same corporate_currency_account, beneficiary_reference, reseller_fee, sender_reference, and payment_purpose you used here -- it does not take buy_currency, sell_currency, amount, fixed_side, or corporate_reference; those only mint the quote. Never submit without that approval, and never tell the user the payment was made until submit_fx_payment_instruction succeeds -- it will still need approval in the Sokin portal afterward.
Reply as data
{
  "draft": {
    "corporate_currency_account": "CCA-1001",
    "beneficiary_reference": "BEN-3301",
    "quote_id": "FXQ-1a2b3c",
    "reseller_fee": null,
    "sender_reference": "INV-4471",
    "payment_purpose": null
  },
  "currency": "GBP",
  "buyCurrency": "USD",
  "quoteCreatedAt": "2026-09-16T10:02:00+00:00",
  "quoteExpiresAt": "2026-09-16T10:12:00+00:00",
  "idempotencyKey": "9c21ab4f-7c4e-4a2b-9f31-6d0e2a5b8c14",
  "quoteRequest": {
    "buy_currency": "USD",
    "sell_currency": "GBP",
    "amount": "100.00",
    "fixed_side": "SELL",
    "corporate_reference": "CORP-8842"
  }
}
Note the draft carries a quote_id and no amounts: the amounts come from the
quote, and the currencies and amount you passed exist only to mint it. Pass
that quote_id through unchanged. quoteRequest echoes those pricing
arguments back so the card can check the draft's status later without
minting a new quote.
Coming back to the conversation later works as it does for
create_payment_instruction: the card checks with Sokin first, and
reopening it mints no new quote. (Some hosts reopen a card without its
stored result; the card then prepares the payment again, which does mint
one.)
The quote can still expire after you approve. The backend re-checks it
when a human approves the instruction in the Sokin portal, which runs at human
pace. If it has lapsed by then, the instruction comes back Rejected with a
reason — not as an error from either tool. quoteExpiresAt is what the card
counts down.

submit_fx_payment_instruction#

Sends the FX payment. This moves real money — it converts and pays a
beneficiary in one motion, and cannot be cancelled once submitted.
Only accepts an idempotency key and quote id that
create_fx_payment_instruction minted, with arguments matching the draft that
was reviewed. On a client with cards the Approve button calls it. On a client
without, calling it directly after the user approves in chat is the intended
path, not a bypass
— that is what the prepare step's text asks you to do.
TakesRequiredWhat it is
idempotency_keyyesThe key from create_fx_payment_instruction
corporate_currency_accountyesMust match the reviewed draft
beneficiary_referenceyesMust match the reviewed draft
quote_idyesFrom the draft's quote_id — the quote the prepare step minted, not one you choose
reseller_feenoMust match the reviewed draft
sender_referencenoMust match the reviewed draft
payment_purposenoMust match the reviewed draft
Request
{
  "name": "submit_fx_payment_instruction",
  "arguments": {
    "idempotency_key": "9c21ab4f-7c4e-4a2b-9f31-6d0e2a5b8c14",
    "corporate_currency_account": "CCA-1001",
    "beneficiary_reference": "BEN-3301",
    "quote_id": "FXQ-1a2b3c",
    "sender_reference": "INV-4471"
  }
}
Reply
FX payment submitted: instruction request IR-FXPayment-77 from CCA-1 to BEN-1. This is an instruction-request reference, not a completed payment -- it now needs human approval in the Sokin portal before it is processed. Check the outcome via get_instruction_request.
Reply as data
{"data": {"externalReference": "IR-FXPayment-77"}}
The approval step is named unconditionally in this text on purpose.
Every company gets an approval rule routing MCP-originated instructions to a
human sign-off in the Sokin portal, so a submitted payment normally needs one
before it is processed. get_instruction_request is how you find out when
that happens.
Re-submitting the same draft under the same key sends nothing again: the tool
reads the original payment's state and reports it, in
get_instruction_request's shape rather than the single-field one above. Code
parsing this must tolerate both.

get_fx_quote#

Asks what an exchange rate is. Never books anything and never moves money.
What you get back depends on your client:
A client with interactive cards gets a card covering the whole
flow — request, quote, settle — with a countdown. This call alone creates no
quote; the card's Get Quote button does. Do not report a rate from this
call by itself.
A client without gets no rate and no quote: nothing is created. It gets
your request echoed back plus a link to the Sokin portal, where you see the
live rate and make the trade.
Why it works that way: a real quote expires in as little as two minutes,
set by whoever priced it. Creating one before a person has seen it would burn
most of that window before anyone could act.
TakesRequiredWhat it is
buy_currencyyesWhat you are buying, e.g. "USD"
sell_currencyyesWhat you are selling, e.g. "GBP"
amountyesThe amount of whichever side fixed_side names
fixed_sideno"BUY" or "SELL". Default "SELL"
corporate_referenceyesWhich company to quote for. From list_corporates
corporate_currency_accountnoThe account a later conversion would come from
card_idnoSent only by the card itself, to check what already happened on it. Leave it unset
Request
{
  "name": "get_fx_quote",
  "arguments": {
    "buy_currency": "USD",
    "sell_currency": "GBP",
    "amount": "1000.00",
    "fixed_side": "SELL",
    "corporate_reference": "CORP-8842",
    "corporate_currency_account": "CCA-1001"
  }
}
Reply, with cards — an acknowledgement, not a rate:
Ready to quote GBP -> USD, 1000.00 on the sell side. No quote has been created yet -- the card's Get Quote button mints one.
The corporateCurrencyAccountDisplay field below is the account's resolved
label, shown on the card's requested state. It only appears when
corporate_currency_account is passed; if the account read fails it
becomes GBP account (number unavailable) and the card still renders.
Reply as data
{
  "buyCurrency": "USD",
  "sellCurrency": "GBP",
  "amount": "1000.00",
  "fixedSide": "SELL",
  "corporateReference": "CORP-8842",
  "corporateCurrencyAccount": "CCA-1001",
  "cardId": "0f8fad5b-d9cb-469f-a165-70867728950e",
  "corporateCurrencyAccountDisplay": "GBP account 12345678"
}
cardId is the card's own id. It is what lets the card remember its quote.
Coming back to the conversation later. When you reopen a chat, the card
checks with Sokin what already happened on it before it offers anything.
That check prices nothing and sends nothing. A settled conversion shows as
submitted, with its IR- reference, and the card carries on tracking it,
including a wait for approval in the Sokin dashboard if your team's approval
rules apply. A
quote that is still live comes back with its countdown and Settle button. A
quote that expired with no settlement on record offers Get Quote again. If a settlement may
still be going through, or ended without a clear answer, the card offers
neither button and asks you to check first. If the check fails, the card says it couldn't check, with Try again,
and offers neither button. (Some hosts reopen a card without its stored
result. The card then starts again as a new card, with no memory of the old
one.)
Reply, without cards — no quote, no rate, just the portal link:
This client cannot show the FX quote card, so no quote was created and no rate is shown. Open https://portal.uat.sokin.com/v2/transfer/currency-exchange to see the live rate and execute GBP -> USD for 1000.00 (SELL side) for corporate CORP-8842. Do not try to quote or settle this through the assistant.
Reply as data
{
  "buyCurrency": "USD",
  "sellCurrency": "GBP",
  "amount": "1000.00",
  "fixedSide": "SELL",
  "corporateReference": "CORP-8842",
  "indicative": true,
  "quoteCreated": false,
  "dashboardUrl": "https://portal.uat.sokin.com/v2/transfer/currency-exchange"
}
A malformed request (same currency on both sides, a bad currency code, or an
amount that is zero, negative, over 2 decimals or above 9999999999) is
refused up front with an error naming the field, before anything is sent.

create_fx_quote#

Creates a real, short-lived exchange rate and holds the conversion ready.
The FX card's Get Quote button runs this — do not call it directly; start
with get_fx_quote. Only available on a client with interactive cards.
It moves no money, but it is a real action: every call prices a live quote.
How long a quote lasts is decided by whoever priced it, and it varies by
about five times between currency pairs
— some expire in two minutes, some
in around ten. Trust only the expiresAt in the result. Never assume a
duration, and never ask for a quote speculatively or twice for the same
request.
Takes the same currency arguments as get_fx_quote, plus optional
reseller_fee and beneficiary_external_reference. The card also sends its
own card_id, so that it remembers the quote if you come back to the
conversation later. If a conversion from that card is already settled, or
may still be going through, no new quote is created and the card shows its
status instead. If you leave the account
out, the company's own account in the currency being sold is found
automatically when there is exactly one; with none or several, the card
appears without a Settle option.
Request
{
  "name": "create_fx_quote",
  "arguments": {
    "buy_currency": "USD",
    "sell_currency": "GBP",
    "amount": "1000.00",
    "fixed_side": "SELL",
    "corporate_reference": "CORP-8842",
    "corporate_currency_account": "CCA-1001"
  }
}
Reply
Quote QT-88213 created: 1000.00 GBP -> 1187.40 USD at rate 1.18740. Expires at 2026-08-27T14:34:00+00:00 -- settle it before then, or ask for a fresh quote once it expires. The 2.50 GBP FX fee is included in the 1000.00 GBP sold; 1187.40 USD is the amount that settles. Settling from GBP account 12345678.
Reply as data
{
  "quoteId": "QT-88213",
  "idempotencyKey": "7c2e9b41-5a08-4d6f-b3c2-91ef0a4d8b52",
  "currencyPair": "GBP/USD",
  "fxRate": "1.18740",
  "fixedSide": "SELL",
  "buyAmount": "1187.40",
  "buyCurrency": "USD",
  "sellAmount": "1000.00",
  "sellCurrency": "GBP",
  "fxFees": "2.50",
  "createdAt": "2026-08-27T14:32:00+00:00",
  "expiresAt": "2026-08-27T14:34:00+00:00",
  "corporateCurrencyAccount": "CCA-1001",
  "corporateCurrencyAccountDisplay": "GBP account 12345678",
  "resellerFee": null
}

settle_fx_quote#

Carries out the conversion. This moves real money between two of the
company's own accounts.
Your Settle click on the FX card runs this. Do not call it directly, and never
reuse a key from a different quote — a reused key reports the original
conversion and ignores anything you changed.
Refused outright on a client that does not render interactive cards, before
anything is sent:
Error: this session has not declared MCP UI support, so real-money FX settlement is refused here. Direct the user to the Sokin dashboard to execute this trade instead.
TakesRequiredWhat it is
idempotency_keyyesThe key from create_fx_quote
corporate_currency_accountyesMust match the quote you saw
quote_idyesMust match the quote you saw
reseller_feenoMust match the quote you saw
Request
{
  "name": "settle_fx_quote",
  "arguments": {
    "idempotency_key": "7c2e9b41-5a08-4d6f-b3c2-91ef0a4d8b52",
    "corporate_currency_account": "CCA-1001",
    "quote_id": "QT-88213"
  }
}
Reply
FX conversion submitted: instruction request IR-FX-9911 for account CCA-1001. This is a submission receipt, not confirmation the conversion completed -- check get_instruction_request for its actual outcome.
Reply as data
{"data": {"externalReference": "IR-FX-9911", "status": "Created"}}
Remember that Created means still processing. Use
get_instruction_request for the outcome — it is a read, and it answers
however long ago the conversion happened.
Things that stop a conversion, none of which send anything:
Anything different from the quote you reviewed.
A quote that has expired — you get the real reason, and can ask for a fresh
one.
A second attempt while the first is still in flight.
If a conversion's outcome is genuinely unknown — a timeout, say — the tool
says exactly that rather than guessing, and tells you to check before
retrying. Submitting a conversion is also not the same as it being approved:
whether it needs a further sign-off depends on the company's own approval
rules in the Sokin platform.

visualize#

Draws a diagram or renders rich text inside the chat. Needs a client with
interactive cards. Calls no Sokin service.
Most read tools have no card of their own, so this is how a result gets a
picture: the assistant reads the data, writes a diagram from it, and passes it
here.
TakesRequiredWhat it is
markdownyesMarkdown to render. Code blocks tagged mermaid become diagrams
titlenoThe card's heading
Request
{
  "name": "visualize",
  "arguments": {
    "title": "Balances by currency",
    "markdown": "```mermaid\npie title Balances\n  \"GBP\" : 48200\n  \"USD\" : 12000\n```"
  }
}
Reply
The card itself, rendered in the chat. There is no summary sentence — the text
half of the reply is the markdown you passed, echoed back unchanged.
Reply as data
{"title": "Balances by currency", "markdown": "```mermaid\npie title Balances\n  \"GBP\" : 48200\n  \"USD\" : 12000\n```"}
The card is about 600 pixels wide with no height limit, so top-to-bottom
diagrams work better than left-to-right, and about 10 to 15 boxes is the
practical maximum before it gets cramped.

get_documentation_links#

Hands back the web addresses of Sokin's own documentation, so an assistant can
read the real contract instead of guessing. Links only — never the content.
The answer is specific to the environment you are connected to, so a UAT
session gets UAT's documentation.
TakesRequiredWhat it is
topicno"b2b_api", "mcp_server", or "all". Default "all"
Request
{"name": "get_documentation_links", "arguments": {"topic": "all"}}
Reply
Sokin documentation for the MCP server at https://mcp.uat.sokin.com.
DocumentationURLWhat's there
Sokin B2B API referencehttps://docs.sokin.com/llms.txtThe Sokin B2B REST API this MCP server calls: every endpoint, authentication, API versions, and request/response schemas
Sokin MCP server referencehttps://docs.sokin.com/sokin-mcp-10453423f0This MCP server: the tools it exposes, what each returns, and how to connect a client
Sokin B2B API reference also has a human-readable page at https://docs.sokin.com. Append .md to any page URL listed in the index at https://docs.sokin.com/llms.txt to fetch it as raw markdown; the API pages include the OpenAPI spec.
Reply as data
{
  "mcpServerUrl": "https://mcp.uat.sokin.com",
  "documentation": [
    {
      "topic": "b2b_api",
      "title": "Sokin B2B API reference",
      "url": "https://docs.sokin.com/llms.txt",
      "alternateUrl": "https://docs.sokin.com",
      "summary": "The Sokin B2B REST API this MCP server calls: every endpoint, authentication, API versions, and request/response schemas"
    },
    {
      "topic": "mcp_server",
      "title": "Sokin MCP server reference",
      "url": "https://docs.sokin.com/sokin-mcp-10453423f0",
      "alternateUrl": null,
      "summary": "This MCP server: the tools it exposes, what each returns, and how to connect a client"
    }
  ]
}
A topic with nothing published yet still gets a row, with the reason in place
of a link, rather than being left out.
The URL it hands back is the page you are reading. These guides went live
on docs.sokin.com on 2026-09-17, and UAT is configured to point at them, so an
assistant that asks for the documentation gets the real address. A deployment
whose tool set these pages do not describe is left unconfigured on purpose —
there the row reads (none available) and says why, which is more useful than
a link that misdescribes what the client is connected to.

play_cheetah_dash#

An endless-runner game, rendered in the chat. Takes nothing and calls no Sokin
service. Needs a client with interactive cards.
Request
{"name": "play_cheetah_dash", "arguments": {}}
Reply
Cheetah Dash is ready -- use Space or tap to jump, again mid-air to double jump.
There is no Reply as data below, because this is the one tool that answers
with just a line of text and no structured data at all.

Starter project ideas#

All built on read-only tools — nothing moves money. Say the prompt in plain
English and let the client pick the tools.
IdeaToolsPrompt to try
Multi-currency balance dashboardlist_corporates, list_accounts"Build a dashboard of all my Sokin balances by currency, with a bar chart of the biggest five."
FX quote checkerget_fx_quote"Make a page where I enter an amount and from/to currency and it shows the current Sokin FX quote."
Transaction history explorerget_account_ledger"Build a screen to pick an account and list its recent transactions, newest first, with a date filter."
Beneficiary directorylist_beneficiaries"Create a searchable directory of my saved beneficiaries with currency and country."
Payment pre-flight checkervalidate_payment_instruction"Let me paste payment details and flag any problems, without submitting anything."
The last one is the stretch, and it is still safe:
validate_payment_instruction saves nothing and moves no money.

Going deeper#

Exact response schemas, field by field, every possible value of every
coded field, and the error and pagination contracts: Sokin MCP —
Technical Docs
.
The Sokin B2B REST API underneath: https://docs.sokin.com, or
https://docs.sokin.com/llms.txt for the machine-readable index.
From inside a chat, ask for the documentation links and
get_documentation_links will return the ones for the environment you are
actually on.
Modified at 2026-10-07 11:36:01
Previous
Sokin MCP - Setup and Usage Guide
Next
Sokin MCP - Technical Docs
Built with