From 03aa43df4dd49016e6440b38c3252567fa059ee2 Mon Sep 17 00:00:00 2001 From: Jason Date: Fri, 13 Nov 2026 17:30:35 +0100 Subject: [PATCH 1/4] docs(routes): add FX swap guide and example --- src/pages/docs/routes.mdx | 2 +- src/pages/docs/routes/examples/fx.mdx | 108 ++++++++++++++++++++++++++ src/pages/docs/routes/fx.mdx | 101 ++++++++++++++++++++++++ vocs.config.ts | 5 ++ 4 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 src/pages/docs/routes/examples/fx.mdx create mode 100644 src/pages/docs/routes/fx.mdx diff --git a/src/pages/docs/routes.mdx b/src/pages/docs/routes.mdx index e2934bfd2..0a0c67c29 100644 --- a/src/pages/docs/routes.mdx +++ b/src/pages/docs/routes.mdx @@ -31,7 +31,7 @@ Routes is in beta. [Contact Tempo](https://tempo.xyz/contact) to discuss access A route connects a source network and token to a destination network and token. Routes prepares the funding instructions or source-chain calls, then tracks delivery to the recipient. A successful source transaction starts the route; your app confirms completion at the destination. -For payments that stay on Tempo, use [Send payments](/docs/guide/payments/send-a-payment) or [Swap stablecoins](/docs/guide/stablecoin-dex/executing-swaps) instead. +For payments that stay on Tempo, use [Send payments](/docs/guide/payments/send-a-payment) or [Swap stablecoins](/docs/guide/stablecoin-dex/executing-swaps) instead. Routes also swaps stablecoins between currencies on Tempo through [FX routes](/docs/routes/fx). diff --git a/src/pages/docs/routes/examples/fx.mdx b/src/pages/docs/routes/examples/fx.mdx new file mode 100644 index 000000000..a6bf7802e --- /dev/null +++ b/src/pages/docs/routes/examples/fx.mdx @@ -0,0 +1,108 @@ +--- +title: "FX swap example" +seoTitle: "Routes FX swap example | Tempo Docs" +description: Run an FX swap on Tempo from a terminal with curl and jq, from the account check to the completed transfer. +--- + +# FX swap example + +Run every step of [Swap currencies](/docs/routes/fx) from a terminal. That guide explains each step; this page keeps the commands together. You sign the source calls with your own signer. + +## Set the FX example variables + +Set `TEMPO_API_KEY` to a key with `routes:read`, `routes:write`, and `data:read` from [Set up Routes](/docs/routes/api-setup). Then set: + +| Variable | Value | +| --- | --- | +| `SOURCE_TOKEN`, `DESTINATION_TOKEN` | Token keys on Tempo, such as `ousd` and `eurce`. | +| `FX_ACCOUNT` | The listed account that sends and receives the swap. | +| `FX_AMOUNT` | Source amount in base units. `1000000` is 1 OUSD. | +| `POOL_ADDRESS`, `PAIR_INDEX`, `SPONSOR_ID` | The route's pool, pair, and list. Tempo provides them. | + +## Swap OUSD for EURC.e + +:::::steps + +### Check the account + +```bash +curl --get \ + "https://api.tempo.xyz/v1/prop-amms/${POOL_ADDRESS}/pairs/${PAIR_INDEX}/accounts/${FX_ACCOUNT}/access" \ + --header "tempo-api-key: ${TEMPO_API_KEY}" \ + --data-urlencode "sponsorId=${SPONSOR_ID}" \ + --data-urlencode 'chainId=4217' +``` + +Continue when the response has `"allowed": true`. + +### Get a quote + +```bash +curl --get 'https://api.tempo.xyz/v1/routes/transfers/quote' \ + --header "tempo-api-key: ${TEMPO_API_KEY}" \ + --data-urlencode 'sourceChain=tempo' \ + --data-urlencode "sourceToken=${SOURCE_TOKEN}" \ + --data-urlencode 'destinationChain=tempo' \ + --data-urlencode "destinationToken=${DESTINATION_TOKEN}" \ + --data-urlencode "sender=${FX_ACCOUNT}" \ + --data-urlencode "recipient=${FX_ACCOUNT}" \ + --data-urlencode "amount=${FX_AMOUNT}" \ + --data-urlencode 'mode=exactSource' +``` + +The quote expires within seconds. Run the next two steps right after it. + +### Create the transfer + +Set `ROUTE_REQUEST_KEY` to the [idempotency key](/docs/routes/api-setup#set-idempotency-keys) for this swap. + +```bash +jq -n \ + --arg sourceToken "$SOURCE_TOKEN" \ + --arg destinationToken "$DESTINATION_TOKEN" \ + --arg sender "$FX_ACCOUNT" \ + --arg recipient "$FX_ACCOUNT" \ + --arg amount "$FX_AMOUNT" \ + '$ARGS.named + {sourceChain: "tempo", destinationChain: "tempo", mode: "exactSource"}' \ + > fx-request.json + +curl --fail-with-body 'https://api.tempo.xyz/v1/routes/transfers' \ + --header "tempo-api-key: ${TEMPO_API_KEY}" \ + --header "idempotency-key: ${ROUTE_REQUEST_KEY}" \ + --header 'Content-Type: application/json' \ + --data @fx-request.json \ + --output fx-transfer.json + +jq '{id, expiresAt: .quote.expiresAt, calls: .action.calls}' fx-transfer.json +``` + +The output lists the transfer `id`, its expiry, and the two calls to sign. + +### Sign and submit + +Submit both calls from `FX_ACCOUNT` before that expiry, as one Tempo Transaction. Keep the transaction hash. + +### Register the source transaction + +Set `ROUTE_TRANSFER_ID` to the returned `id` and `SOURCE_TRANSACTION` to the hash. + +```bash +jq -n --arg transactionHash "$SOURCE_TRANSACTION" '$ARGS.named' > fx-source.json + +curl --fail-with-body \ + "https://api.tempo.xyz/v1/routes/transfers/${ROUTE_TRANSFER_ID}/source-transactions" \ + --header "tempo-api-key: ${TEMPO_API_KEY}" \ + --header 'Content-Type: application/json' \ + --data @fx-source.json +``` + +### Confirm the swap + +```bash +curl "https://api.tempo.xyz/v1/routes/transfers/${ROUTE_TRANSFER_ID}" \ + --header "tempo-api-key: ${TEMPO_API_KEY}" +``` + +The swap is done when `status` is `completed`. + +::::: diff --git a/src/pages/docs/routes/fx.mdx b/src/pages/docs/routes/fx.mdx new file mode 100644 index 000000000..7684ed800 --- /dev/null +++ b/src/pages/docs/routes/fx.mdx @@ -0,0 +1,101 @@ +--- +title: Swap currencies +seoTitle: Swap Currencies | Tempo Docs +description: Swap a stablecoin in one currency for a stablecoin in another on Tempo, such as OUSD for EURC.e, with a Routes transfer. +--- + +import { OpenApi } from 'vocs' + +# Swap currencies + +Swap a stablecoin in one currency for a stablecoin in another on Tempo, such as OUSD for EURC.e. + +An FX route is a [Routes](/docs/routes) route with both ends on Tempo and a different currency on each end. You quote it, create a transfer, and sign the returned calls, as in [Make a transfer](/docs/routes/transfers). The source transaction is the swap, so the sender receives the destination stablecoin when it confirms. + +:::warning +FX routes fill swaps only from listed accounts. [Contact Tempo](https://tempo.xyz/contact) to enable a route and list your accounts. +::: + +## How FX routes differ + +- **Listed senders.** Each FX route has a list of accounts that can swap. A quote for an unlisted `sender` returns `404`. +- **Same sender and recipient.** The swap pays the account that sends it. Set `recipient` to the `sender` address. +- **Short quotes.** A quote expires within seconds. The swap call carries that expiry and reverts after it. +- **Price in the amounts.** The quoted source and destination amounts include the price. The route adds no separate fee. + +## Prepare an FX swap + +Before you start, have: + +- A Tempo API key with `routes:read`, `routes:write`, and `data:read` from [Set up Routes](/docs/routes/api-setup). +- An FX route from [Supported routes](/docs/routes/networks), such as OUSD to EURC.e on Tempo. +- The route's pool address, pair index, and sponsor ID. Tempo provides them when it enables your access. +- A signer for a listed account that holds the source stablecoin and a fee token. + +The [FX swap example](/docs/routes/examples/fx) runs every step from a terminal. + +## Swap one stablecoin for another + +:::::steps + +### Check the account + + + +A listed account returns `"member": true` and `"allowed": true`. Otherwise `reason` and `explanation` name what's missing. + +### Get a quote + +Send the account as both `sender` and `recipient`. Amounts are in base units. + + + +Show the source amount, the destination amount, and `destinationAmountMin`. Create the transfer before `quote.expiresAt`. + +### Create the transfer + +Send the same inputs with an [idempotency key](/docs/routes/api-setup#set-idempotency-keys). + + + +Save the transfer `id` and its `action`. For an exact-source swap, `action.calls` holds two calls. The first approves the source stablecoin. The second swaps it. + +### Sign and submit + +Submit both calls from the sender account before the transfer's `quote.expiresAt`. Send them as one [Tempo Transaction](/docs/guide/tempo-transaction) so the approval and the swap land together. [Tempo Wallet](/docs/accounts/wallets#connect-tempo-wallet) does this for you. + +If the swap call reverts after the expiry, create a new transfer from a new quote. + +### Register the source transaction + + + +The API checks the swap onchain and links it to the transfer. + +### Confirm the swap + + + +Keep the payment pending until `status` is `completed`. Reconcile `destinationAmount` against the account's balance change. + +::::: + +## Manage the account list + +A list has one admin account. The admin adds or removes up to 50 accounts in one request. The key needs `propamm:write`, which an organization owner issues. + + + +The API checks the change and returns an unsigned `transaction`. The admin account signs and sends it. The API doesn't sign or send it. To remove accounts, send the same request with `"allowed": false`. + +## FX quote errors + +A refused quote returns `404` with code `quote_not_available`. Its `details` name the field to change. + +| `details` path | Cause | Fix | +| --- | --- | --- | +| `sender` | The account isn't on the route's list. | Check the account, then ask the list admin to add it. | +| `recipient` | The recipient differs from the sender. | Set `recipient` to the `sender` address. | +| `amount` | The pool can't fill that size now. | Lower the amount or retry later. | + +Continue with [Track delivery](/docs/routes/delivery) for every transfer status. diff --git a/vocs.config.ts b/vocs.config.ts index cf47bef52..8144ec71f 100644 --- a/vocs.config.ts +++ b/vocs.config.ts @@ -852,11 +852,16 @@ export default defineConfig({ { text: 'Track delivery', link: '/docs/routes/delivery' }, ], }, + { + text: 'FX', + items: [{ text: 'Swap currencies', link: '/docs/routes/fx' }], + }, { text: 'cURL examples', items: [ { text: 'Deposit address example', link: '/docs/routes/examples/deposits' }, { text: 'Transfer example', link: '/docs/routes/examples/transfers' }, + { text: 'FX swap example', link: '/docs/routes/examples/fx' }, ], }, ] From 1b1a7fd6c271c771719aa13ede34126b3165cb6c Mon Sep 17 00:00:00 2001 From: Jason Date: Fri, 13 Nov 2026 19:06:57 +0100 Subject: [PATCH 2/4] docs(routes): document organization lists and token-based whitelisting --- src/pages/docs/routes/examples/fx.mdx | 45 ++++++++++++-- src/pages/docs/routes/fx.mdx | 90 ++++++++++++++++++++++++--- 2 files changed, 121 insertions(+), 14 deletions(-) diff --git a/src/pages/docs/routes/examples/fx.mdx b/src/pages/docs/routes/examples/fx.mdx index a6bf7802e..a3528171a 100644 --- a/src/pages/docs/routes/examples/fx.mdx +++ b/src/pages/docs/routes/examples/fx.mdx @@ -14,10 +14,10 @@ Set `TEMPO_API_KEY` to a key with `routes:read`, `routes:write`, and `data:read` | Variable | Value | | --- | --- | -| `SOURCE_TOKEN`, `DESTINATION_TOKEN` | Token keys on Tempo, such as `ousd` and `eurce`. | +| `SOURCE_TOKEN`, `DESTINATION_TOKEN` | Token addresses on Tempo from the [route directory](/docs/routes/networks). OUSD is `0x20c0000000000000000000006a37da5c996874be`. EURC.e is `0x20c0000000000000000000001621e21f71cf12fb`. | | `FX_ACCOUNT` | The listed account that sends and receives the swap. | | `FX_AMOUNT` | Source amount in base units. `1000000` is 1 OUSD. | -| `POOL_ADDRESS`, `PAIR_INDEX`, `SPONSOR_ID` | The route's pool, pair, and list. Tempo provides them. | +| `POOL_ADDRESS` | The route's pool. Tempo provides it. | ## Swap OUSD for EURC.e @@ -27,13 +27,13 @@ Set `TEMPO_API_KEY` to a key with `routes:read`, `routes:write`, and `data:read` ```bash curl --get \ - "https://api.tempo.xyz/v1/prop-amms/${POOL_ADDRESS}/pairs/${PAIR_INDEX}/accounts/${FX_ACCOUNT}/access" \ + "https://api.tempo.xyz/v1/prop-amms/${POOL_ADDRESS}/accounts/${FX_ACCOUNT}/access" \ --header "tempo-api-key: ${TEMPO_API_KEY}" \ - --data-urlencode "sponsorId=${SPONSOR_ID}" \ + --data-urlencode "tokens=${SOURCE_TOKEN},${DESTINATION_TOKEN}" \ --data-urlencode 'chainId=4217' ``` -Continue when the response has `"allowed": true`. +Continue when the response has `"allowed": true`. The request checks your organization's saved list. If your organization uses the pool's default list, add `--data-urlencode 'sponsorId=0'`. ### Get a quote @@ -106,3 +106,38 @@ curl "https://api.tempo.xyz/v1/routes/transfers/${ROUTE_TRANSFER_ID}" \ The swap is done when `status` is `completed`. ::::: + +## Add accounts to the list + +The list's admin account adds `FX_ACCOUNT` before its first swap. Set `LIST_ADMIN` to the admin account's address. The key also needs `propamm:write`. + +:::::steps + +### Prepare the change + +```bash +jq -n \ + --arg sender "$LIST_ADMIN" \ + --arg account "$FX_ACCOUNT" \ + --arg sourceToken "$SOURCE_TOKEN" \ + --arg destinationToken "$DESTINATION_TOKEN" \ + '{sender: $sender, action: {type: "setMembers", tokens: [$sourceToken, $destinationToken], accounts: [$account], allowed: true}}' \ + > fx-access.json + +curl --fail-with-body \ + "https://api.tempo.xyz/v1/prop-amms/${POOL_ADDRESS}/access/prepare" \ + --header "tempo-api-key: ${TEMPO_API_KEY}" \ + --header 'Content-Type: application/json' \ + --data @fx-access.json \ + --output fx-access-change.json + +jq '{effect, transaction}' fx-access-change.json +``` + +The output states the change and the unsigned transaction. The change applies to your organization's saved list. + +### Sign and send the transaction + +Send `transaction.data` to `transaction.to` from `LIST_ADMIN`. The API doesn't sign or send it. Run the account check again after the transaction confirms. + +::::: diff --git a/src/pages/docs/routes/fx.mdx b/src/pages/docs/routes/fx.mdx index 7684ed800..e314d1f98 100644 --- a/src/pages/docs/routes/fx.mdx +++ b/src/pages/docs/routes/fx.mdx @@ -13,23 +13,36 @@ Swap a stablecoin in one currency for a stablecoin in another on Tempo, such as An FX route is a [Routes](/docs/routes) route with both ends on Tempo and a different currency on each end. You quote it, create a transfer, and sign the returned calls, as in [Make a transfer](/docs/routes/transfers). The source transaction is the swap, so the sender receives the destination stablecoin when it confirms. :::warning -FX routes fill swaps only from listed accounts. [Contact Tempo](https://tempo.xyz/contact) to enable a route and list your accounts. +FX routes fill swaps only from listed accounts. [Contact Tempo](https://tempo.xyz/contact) to enable a route and set up your organization's account list. ::: ## How FX routes differ -- **Listed senders.** Each FX route has a list of accounts that can swap. A quote for an unlisted `sender` returns `404`. +- **Listed senders.** Only accounts on your organization's [account list](#account-lists-on-fx-routes) can swap. A quote for an unlisted `sender` returns `404`. - **Same sender and recipient.** The swap pays the account that sends it. Set `recipient` to the `sender` address. - **Short quotes.** A quote expires within seconds. The swap call carries that expiry and reverts after it. - **Price in the amounts.** The quoted source and destination amounts include the price. The route adds no separate fee. +## Account lists on FX routes + +An account list names the accounts that can swap on one pair of a pool. The pool is the contract that fills an FX route's swaps. A pair is the two stablecoins it swaps. + +- **Default list.** Every pool has a default list with sponsor ID `0`. The pool's owner manages it. +- **Organization lists.** A pool can have further lists. Each has its own sponsor ID and its own admin account. + +A swap runs under exactly one list, and the sender must be on that list. Your organization uses either the pool's default list or its own list. Tempo saves that choice for each pool and pair when it enables your access. You can't change it through the API. + +Routes quotes and creates swaps for your organization's API keys under the saved list. Quote and transfer requests take no list parameter. An organization with no saved list uses the pool's default list. + +Once your organization has its own list on a pair, the default list no longer applies to your keys there. An account that's only on the default list gets no quote through your keys on that pair. + ## Prepare an FX swap Before you start, have: - A Tempo API key with `routes:read`, `routes:write`, and `data:read` from [Set up Routes](/docs/routes/api-setup). -- An FX route from [Supported routes](/docs/routes/networks), such as OUSD to EURC.e on Tempo. -- The route's pool address, pair index, and sponsor ID. Tempo provides them when it enables your access. +- An FX route from [Supported routes](/docs/routes/networks), such as OUSD to EURC.e on Tempo, and its two token addresses. +- The route's pool address. Tempo provides it when it enables your access. - A signer for a listed account that holds the source stablecoin and a fee token. The [FX swap example](/docs/routes/examples/fx) runs every step from a terminal. @@ -40,13 +53,22 @@ The [FX swap example](/docs/routes/examples/fx) runs every step from a terminal. ### Check the account - +Name the pair by its two token addresses, in either order. + +```bash +curl --get \ + 'https://api.tempo.xyz/v1/prop-amms//accounts//access' \ + --header "tempo-api-key: $TEMPO_API_KEY" \ + --data-urlencode 'tokens=,' +``` -A listed account returns `"member": true` and `"allowed": true`. Otherwise `reason` and `explanation` name what's missing. +The API checks the account against your organization's saved list. A listed account returns `"member": true`, `"allowed": true`, and `"reason": "allowed"`. Routes runs the same check when it quotes. Otherwise `reason` and `explanation` name what's missing, such as `notListed`. + +Without a saved list, the request returns `400` with code `propamm_invalid`. If your organization uses the pool's default list, add `sponsorId=0`. ### Get a quote -Send the account as both `sender` and `recipient`. Amounts are in base units. +Send the account as both `sender` and `recipient`. Amounts are in base units. Routes quotes under your organization's list, so the request names no pool or list. @@ -84,9 +106,59 @@ Keep the payment pending until `status` is `completed`. Reconcile `destinationAm A list has one admin account. The admin adds or removes up to 50 accounts in one request. The key needs `propamm:write`, which an organization owner issues. +:::::steps + +### Prepare the change + +Send the list's admin account as `sender`. Name the pair by its two token addresses, in either order. + +```json +{ + "sender": "", + "action": { + "type": "setMembers", + "tokens": ["", ""], + "accounts": [""], + "allowed": true + } +} +``` + -The API checks the change and returns an unsigned `transaction`. The admin account signs and sends it. The API doesn't sign or send it. To remove accounts, send the same request with `"allowed": false`. +The API applies the change to your organization's saved list. It simulates the change and returns an unsigned `transaction`. `effect` names the resolved pair index and sponsor ID, such as `List 1 account(s) on pair 0 under sponsor 5.` + +To remove accounts, send the same request with `"allowed": false`. + +### Sign and send the transaction + +The admin account sends a transaction to `transaction.to` with `transaction.data`. The API doesn't sign or send it. + +### Confirm the listing + +Repeat the [account check](#check-the-account) after the transaction confirms. A listed account returns `"allowed": true`. + +::::: + +## Name a pair or list + +Requests that omit `sponsorId` use your organization's saved list. Requests with `tokens` resolve the pair from its two token addresses. Name either explicitly when the API can't resolve it, such as a pool with two pairs for the same tokens. + +- **Sponsor ID.** Pass `sponsorId` as a decimal string to name a list, such as `"0"` for the pool's default list. The account check takes it as a query parameter. The access change takes it in `action`. +- **Pair index.** Pass `pairIndex` in place of `tokens` in the access change. Send exactly one of the two. The account check takes the pair index in its path. + + + +## Account list errors + +An access change the API can't resolve or simulate returns one of these errors. The account check returns the same `400` when your organization has no saved list. + +| Status | Code | Cause | Fix | +| --- | --- | --- | --- | +| `400` | `propamm_invalid` | The request omits `sponsorId`, and your organization has no saved list on that pool and pair. | Pass `sponsorId`, or ask Tempo to set the list. | +| `404` | `propamm_not_found` | The pool has no active pair for the two tokens. | Check both token addresses against the route. | +| `409` | `propamm_conflict` | More than one pair swaps the two tokens. | Pass `pairIndex` in place of `tokens`. | +| `409` | `propamm_rejected` | `sender` isn't the list's admin account. | Send the list's admin account as `sender`. | ## FX quote errors @@ -94,7 +166,7 @@ A refused quote returns `404` with code `quote_not_available`. Its `details` nam | `details` path | Cause | Fix | | --- | --- | --- | -| `sender` | The account isn't on the route's list. | Check the account, then ask the list admin to add it. | +| `sender` | The account isn't on your organization's list. | Check the account, then ask the list admin to add it. | | `recipient` | The recipient differs from the sender. | Set `recipient` to the `sender` address. | | `amount` | The pool can't fill that size now. | Lower the amount or retry later. | From 811fabce1ff2eb9a73ebed238e3a009b06f72d81 Mon Sep 17 00:00:00 2001 From: Jason Date: Sat, 14 Nov 2026 00:16:31 +0100 Subject: [PATCH 3/4] docs(routes): show FX values in the swap guide's request samples --- e2e/docs-workspace.test.ts | 2 +- e2e/routes-demo.test.ts | 2 +- scripts/illustrations/linework.mjs | 5 +- src/components/DocsHeader.tsx | 2 +- src/components/DocsSectionNav.tsx | 2 +- src/components/RoutesApiKey.tsx | 2 +- src/components/RoutesTester.tsx | 2 +- src/lib/docs-link-cards.ts | 2 +- src/pages/docs/api.mdx | 2 +- src/pages/docs/api/authentication.mdx | 2 +- src/pages/docs/api/console.mdx | 12 ++-- src/pages/docs/api/console/api-keys.mdx | 8 +-- .../api/console/projects-and-environments.mdx | 2 +- src/pages/docs/api/console/team.mdx | 4 +- .../docs/api/console/usage-and-billing.mdx | 6 +- src/pages/docs/api/fee-payer.mdx | 2 +- src/pages/docs/routes/api-setup.mdx | 2 +- src/pages/docs/routes/fx.mdx | 56 +++++++++++++------ src/pages/docs/routes/test.mdx | 2 +- vercel.json | 4 +- 20 files changed, 71 insertions(+), 50 deletions(-) diff --git a/e2e/docs-workspace.test.ts b/e2e/docs-workspace.test.ts index 8ebeeb07f..ce7a82871 100644 --- a/e2e/docs-workspace.test.ts +++ b/e2e/docs-workspace.test.ts @@ -13,7 +13,7 @@ const utilityLabels = ['API reference', 'Partners'] const externalTools = [ ['MPP', 'https://mpp.dev/'], ['Mercator', 'https://mercator.sh/'], - ['Tempo Console', 'https://console.tempo.xyz/'], + ['Tempo Console', 'https://tempo.xyz/console'], ['Tempo Wallet', 'https://wallet.tempo.xyz/'], ] as const diff --git a/e2e/routes-demo.test.ts b/e2e/routes-demo.test.ts index e246f6e4c..39adc8797 100644 --- a/e2e/routes-demo.test.ts +++ b/e2e/routes-demo.test.ts @@ -1395,7 +1395,7 @@ test.describe('Routes demo without a key', () => { // "here" opens the key panel; the console link is where keys are made. await expect(lifecycle(page).getByRole('link', { name: 'Tempo API console' })).toHaveAttribute( 'href', - 'https://console.tempo.xyz/?to=/:org/api-keys', + 'https://tempo.xyz/console?to=/:org/api-keys', ) await lifecycle(page).getByRole('button', { name: 'here', exact: true }).click() await expect(page.getByLabel('Project API key')).toBeFocused() diff --git a/scripts/illustrations/linework.mjs b/scripts/illustrations/linework.mjs index 5ebf196ac..b94688703 100644 --- a/scripts/illustrations/linework.mjs +++ b/scripts/illustrations/linework.mjs @@ -257,10 +257,7 @@ const partners = illustration( line(366, 228, 384, 228, ink.black), ]), ), - group( - 'Marks', - join([node(200, 132), node(248, 132, 4), node(360, 228, 4), node(392, 228)]), - ), + group('Marks', join([node(200, 132), node(248, 132, 4), node(360, 228, 4), node(392, 228)])), ]), ) diff --git a/src/components/DocsHeader.tsx b/src/components/DocsHeader.tsx index f1a5f9e74..aa779506f 100644 --- a/src/components/DocsHeader.tsx +++ b/src/components/DocsHeader.tsx @@ -60,7 +60,7 @@ const TEMPO_AI_GUIDE_URL = `${DOCS_BASE_PATH}/guide/using-tempo-with-ai` const TEMPO_PLUGIN_URL = `${TEMPO_AI_GUIDE_URL}#install-tempo-plugins` /** Tempo Console. This host forwards to developers.tempo.xyz/console once the console moves there. */ -const CONSOLE_URL = 'https://console.tempo.xyz' +const CONSOLE_URL = 'https://tempo.xyz/console' /** Console session lookup, proxied by this host; its cookie is scoped to `/console` so docs pages never see it. */ const CONSOLE_ME_PATH = '/console/api/v1/me' diff --git a/src/components/DocsSectionNav.tsx b/src/components/DocsSectionNav.tsx index 4ae93ffd6..af8c2da99 100644 --- a/src/components/DocsSectionNav.tsx +++ b/src/components/DocsSectionNav.tsx @@ -49,7 +49,7 @@ const sectionIcons: Partial> = { export const docsExternalTools = [ { label: 'MPP', href: 'https://mpp.dev/', logo: MppLogo }, { label: 'Mercator', href: 'https://mercator.sh/', logo: MercatorLogo }, - { label: 'Tempo Console', href: 'https://console.tempo.xyz/', logo: TempoMark }, + { label: 'Tempo Console', href: 'https://tempo.xyz/console', logo: TempoMark }, { label: 'Tempo Wallet', href: 'https://wallet.tempo.xyz/', logo: TempoMark }, ] diff --git a/src/components/RoutesApiKey.tsx b/src/components/RoutesApiKey.tsx index e8d33fa7c..0dd916077 100644 --- a/src/components/RoutesApiKey.tsx +++ b/src/components/RoutesApiKey.tsx @@ -94,7 +94,7 @@ export function ApiKeyPill({

Add your API key from the{' '} string> = { 'eip155:4217': (hash) => `https://explore.tempo.xyz/tx/${hash}`, 'eip155:42431': (hash) => `https://explore.testnet.tempo.xyz/tx/${hash}`, diff --git a/src/lib/docs-link-cards.ts b/src/lib/docs-link-cards.ts index f6ae82f47..441ec72b7 100644 --- a/src/lib/docs-link-cards.ts +++ b/src/lib/docs-link-cards.ts @@ -161,7 +161,7 @@ export const docsLinkCards = { icon: 'code', links: [ ['Console guide', '/docs/api/console'], - ['Open Tempo Console', 'https://console.tempo.xyz/'], + ['Open Tempo Console', 'https://tempo.xyz/console'], ], }, ], diff --git a/src/pages/docs/api.mdx b/src/pages/docs/api.mdx index b3478076b..49859388e 100644 --- a/src/pages/docs/api.mdx +++ b/src/pages/docs/api.mdx @@ -46,7 +46,7 @@ The API uses `https://api.tempo.xyz/v1`. Most reads work without a key at the [p curl 'https://api.tempo.xyz/v1/blocks' ``` -For your own quota, create an API key in [Tempo Console](https://console.tempo.xyz/?to=/:org/api-keys) and send it in the `tempo-api-key` header. Keep it on your backend. [Authentication](/docs/api/authentication) covers keys, scopes, and paying per request with MPP. +For your own quota, create an API key in [Tempo Console](https://tempo.xyz/console?to=/:org/api-keys) and send it in the `tempo-api-key` header. Keep it on your backend. [Authentication](/docs/api/authentication) covers keys, scopes, and paying per request with MPP. To reconcile a payment, read its [transaction and receipt](/docs/api/transactions) and the [account activity](/docs/api/activities). diff --git a/src/pages/docs/api/authentication.mdx b/src/pages/docs/api/authentication.mdx index 673d649ce..c9f39bb10 100644 --- a/src/pages/docs/api/authentication.mdx +++ b/src/pages/docs/api/authentication.mdx @@ -19,7 +19,7 @@ Choose public access, pay per request with MPP, or authenticate with an API key: Most read endpoints are open to **public** (anonymous) callers, so no credential is required to get started. To raise your throughput, pay per request with [MPP](#mpp-paying-per-request) or send an **API key**. Some endpoints, such as webhook management, require a key. :::info -Create production and sandbox API keys in [Tempo Console](https://console.tempo.xyz/?to=/:org/api-keys). The [console API-key guide](/docs/api/console/api-keys) covers project selection, scopes, storage, rotation, and revocation. +Create production and sandbox API keys in [Tempo Console](https://tempo.xyz/console?to=/:org/api-keys). The [console API-key guide](/docs/api/console/api-keys) covers project selection, scopes, storage, rotation, and revocation. ::: diff --git a/src/pages/docs/api/console.mdx b/src/pages/docs/api/console.mdx index 351d86cf2..f15e6fa0b 100644 --- a/src/pages/docs/api/console.mdx +++ b/src/pages/docs/api/console.mdx @@ -12,7 +12,7 @@ import { Card, Cards } from 'vocs' # Tempo Console -[Tempo Console](https://console.tempo.xyz) is the control UI for your Tempo API account. Use it to organize integrations, issue credentials, monitor API usage and sponsored transactions, configure billing, and manage access to your organization. +[Tempo Console](https://tempo.xyz/console) is the control UI for your Tempo API account. Use it to organize integrations, issue credentials, monitor API usage and sponsored transactions, configure billing, and manage access to your organization. ## How the console is organized @@ -33,23 +33,23 @@ Most console pages show information across the organization. Use the project fil ### Sign in to Tempo Console -Open [console.tempo.xyz](https://console.tempo.xyz/?to=/:org/projects) and sign in. If you have access to more than one organization, choose the organization you want to configure from the organization menu. +Open [tempo.xyz/console](https://tempo.xyz/console?to=/:org/projects) and sign in. If you have access to more than one organization, choose the organization you want to configure from the organization menu. ### Create a project -Select [**Projects**](https://console.tempo.xyz/?to=/:org/projects), then [**New project**](https://console.tempo.xyz/?to=/:org/new). Enter a name that identifies the app or integration using the API, then select **Create project**. +Select [**Projects**](https://tempo.xyz/console?to=/:org/projects), then [**New project**](https://tempo.xyz/console?to=/:org/new). Enter a name that identifies the app or integration using the API, then select **Create project**. Projects hold API keys and provide an attribution boundary for usage. Create separate projects when you need to track or revoke integrations independently. ### Start in sandbox -Open the organization menu and select [**Switch to sandbox**](https://console.tempo.xyz/?to=/:org/projects%3Fenv%3Dsandbox). The amber banner confirms that the console is showing sandbox state. +Open the organization menu and select [**Switch to sandbox**](https://tempo.xyz/console?to=/:org/projects%3Fenv%3Dsandbox). The amber banner confirms that the console is showing sandbox state. Sandbox API keys are restricted to non-mainnet chains. Pass `chainId=testnet` explicitly. Production and sandbox credentials, usage, and billing state are separate. ### Create an API key -Select [**API Keys**](https://console.tempo.xyz/?to=/:org/api-keys%3Fenv%3Dsandbox), then [**New key**](https://console.tempo.xyz/?to=/:org/api-keys/new%3Fenv%3Dsandbox). Choose the project, give the key a descriptive name, and review its scopes before selecting **Create key**. +Select [**API Keys**](https://tempo.xyz/console?to=/:org/api-keys%3Fenv%3Dsandbox), then [**New key**](https://tempo.xyz/console?to=/:org/api-keys/new%3Fenv%3Dsandbox). Choose the project, give the key a descriptive name, and review its scopes before selecting **Create key**. The console selects the available self-service scopes by default. If you clear every scope, the key is created without access to any scoped API. @@ -72,7 +72,7 @@ A sandbox key requires a non-mainnet selector, such as `chainId=testnet`; omitti ### Review usage -Select [**Usage**](https://console.tempo.xyz/?to=/:org/usage%3Fenv%3Dsandbox) and choose your project from the filter. The page reports request volume, errors, sponsored transactions, high-volume routes, and usage by API key for the selected environment. +Select [**Usage**](https://tempo.xyz/console?to=/:org/usage%3Fenv%3Dsandbox) and choose your project from the filter. The page reports request volume, errors, sponsored transactions, high-volume routes, and usage by API key for the selected environment. ::::: diff --git a/src/pages/docs/api/console/api-keys.mdx b/src/pages/docs/api/console/api-keys.mdx index d987a1e91..4f0588dd2 100644 --- a/src/pages/docs/api/console/api-keys.mdx +++ b/src/pages/docs/api/console/api-keys.mdx @@ -17,11 +17,11 @@ You need at least one project before creating a key. ### Choose the environment -Open the organization menu in [Tempo Console](https://console.tempo.xyz/?to=/:org/api-keys) and switch to [**Sandbox**](https://console.tempo.xyz/?to=/:org/api-keys%3Fenv%3Dsandbox) or [**Production**](https://console.tempo.xyz/?to=/:org/api-keys). Check for the amber sandbox banner before continuing. +Open the organization menu in [Tempo Console](https://tempo.xyz/console?to=/:org/api-keys) and switch to [**Sandbox**](https://tempo.xyz/console?to=/:org/api-keys%3Fenv%3Dsandbox) or [**Production**](https://tempo.xyz/console?to=/:org/api-keys). Check for the amber sandbox banner before continuing. ### Open the new-key form -Select [**API Keys**](https://console.tempo.xyz/?to=/:org/api-keys), then [**New key**](https://console.tempo.xyz/?to=/:org/api-keys/new). +Select [**API Keys**](https://tempo.xyz/console?to=/:org/api-keys), then [**New key**](https://tempo.xyz/console?to=/:org/api-keys/new). ### Choose a project and name @@ -110,11 +110,11 @@ Replace the old token in the integration's secret configuration and deploy or re ### Verify the replacement -Make a request with the new key and check the project's [**Usage**](https://console.tempo.xyz/?to=/:org/usage) page. Confirm that requests are attributed to the replacement key. +Make a request with the new key and check the project's [**Usage**](https://tempo.xyz/console?to=/:org/usage) page. Confirm that requests are attributed to the replacement key. ### Revoke the old key -Return to [**API Keys**](https://console.tempo.xyz/?to=/:org/api-keys), open the old key's actions menu, select **Revoke key**, and confirm. Requests using that token stop authenticating. +Return to [**API Keys**](https://tempo.xyz/console?to=/:org/api-keys), open the old key's actions menu, select **Revoke key**, and confirm. Requests using that token stop authenticating. ::::: diff --git a/src/pages/docs/api/console/projects-and-environments.mdx b/src/pages/docs/api/console/projects-and-environments.mdx index e34da0263..931498d1b 100644 --- a/src/pages/docs/api/console/projects-and-environments.mdx +++ b/src/pages/docs/api/console/projects-and-environments.mdx @@ -19,7 +19,7 @@ Each API key belongs to one project. Project attribution lets you filter API key ### Open Projects -Select [**Projects** in Tempo Console](https://console.tempo.xyz/?to=/:org/projects), then select [**New project**](https://console.tempo.xyz/?to=/:org/new). +Select [**Projects** in Tempo Console](https://tempo.xyz/console?to=/:org/projects), then select [**New project**](https://tempo.xyz/console?to=/:org/new). ### Name the project diff --git a/src/pages/docs/api/console/team.mdx b/src/pages/docs/api/console/team.mdx index e22faffb7..4890c5e32 100644 --- a/src/pages/docs/api/console/team.mdx +++ b/src/pages/docs/api/console/team.mdx @@ -27,7 +27,7 @@ Owners and admins can send invitations. Admins can grant only the Member role. ### Open Team -Select [**Team** in Tempo Console](https://console.tempo.xyz/?to=/:org/team). +Select [**Team** in Tempo Console](https://tempo.xyz/console?to=/:org/team). ### Enter the invitation details @@ -57,4 +57,4 @@ Role changes take effect for organization access after the update succeeds. They - **Remove a member:** open the member's actions menu, select **Remove**, and confirm. - **Leave an organization:** open your own actions menu and select **Leave organization**. The last owner cannot leave until another owner exists. -Removing a member does not automatically revoke project API keys. Review [**API Keys**](https://console.tempo.xyz/?to=/:org/api-keys) separately when offboarding someone who managed integration credentials. +Removing a member does not automatically revoke project API keys. Review [**API Keys**](https://tempo.xyz/console?to=/:org/api-keys) separately when offboarding someone who managed integration credentials. diff --git a/src/pages/docs/api/console/usage-and-billing.mdx b/src/pages/docs/api/console/usage-and-billing.mdx index ec4494f50..1e806c21b 100644 --- a/src/pages/docs/api/console/usage-and-billing.mdx +++ b/src/pages/docs/api/console/usage-and-billing.mdx @@ -11,7 +11,7 @@ Tempo Console reports API activity and manages billing for fee sponsorship. Usag ## Review API usage -Select [**Usage**](https://console.tempo.xyz/?to=/:org/usage) to inspect activity in the selected environment. Use the project selector to show all projects or one project. +Select [**Usage**](https://tempo.xyz/console?to=/:org/usage) to inspect activity in the selected environment. Use the project selector to show all projects or one project. The page includes: @@ -38,7 +38,7 @@ Switch to the production or sandbox environment whose billing state you want to ### Open Billing -Select [**Billing**](https://console.tempo.xyz/?to=/:org/billing), then find **Payment methods**. +Select [**Billing**](https://tempo.xyz/console?to=/:org/billing), then find **Payment methods**. ### Add the payment method @@ -68,7 +68,7 @@ Production fee sponsorship requires an active billing source. A spend or sponsor ## Investigate unexpected usage 1. Confirm that the console is showing the correct environment. -2. Open [**Usage**](https://console.tempo.xyz/?to=/:org/usage) and filter to the affected project. +2. Open [**Usage**](https://tempo.xyz/console?to=/:org/usage) and filter to the affected project. 3. Compare the **Routes** and **API keys** sections to find the source. 4. Revoke a credential if its traffic is unexpected. 5. Review the Billing page and adjust spend or sponsorship limits if needed. diff --git a/src/pages/docs/api/fee-payer.mdx b/src/pages/docs/api/fee-payer.mdx index 6731a3567..69690d5e1 100644 --- a/src/pages/docs/api/fee-payer.mdx +++ b/src/pages/docs/api/fee-payer.mdx @@ -45,7 +45,7 @@ Send a testnet payment whose fee Tempo's public fee payer covers. ### Create an API key -Sign in to [Tempo Console](https://console.tempo.xyz/?to=/:org/projects), create an organization and project, add a payment method in [**Billing**](https://console.tempo.xyz/?to=/:org/billing), and create a key in [**API Keys**](https://console.tempo.xyz/?to=/:org/api-keys/new). Console shows the key once. +Sign in to [Tempo Console](https://tempo.xyz/console?to=/:org/projects), create an organization and project, add a payment method in [**Billing**](https://tempo.xyz/console?to=/:org/billing), and create a key in [**API Keys**](https://tempo.xyz/console?to=/:org/api-keys/new). Console shows the key once. The key's environment picks the network. A production key (`tempo:sk:...`) sponsors mainnet transactions. A sandbox key (`tempo_sandbox:sk:...`) sponsors testnet transactions, and the examples on this page use one. Switch environments in the organization picker. To try sponsorship without a key, use the [public testnet fee payer](#public-testnet-fee-payer). diff --git a/src/pages/docs/routes/api-setup.mdx b/src/pages/docs/routes/api-setup.mdx index 38e14aa53..30632a6b4 100644 --- a/src/pages/docs/routes/api-setup.mdx +++ b/src/pages/docs/routes/api-setup.mdx @@ -35,7 +35,7 @@ Routes is in beta. [Contact Tempo](https://tempo.xyz/contact) to discuss access ### Create a Routes API key -Create a key in [Tempo Console](https://console.tempo.xyz/?to=/:org/api-keys) with the scopes your flow needs. Keep it on your backend; it authenticates your application and doesn't sign transactions. +Create a key in [Tempo Console](https://tempo.xyz/console?to=/:org/api-keys) with the scopes your flow needs. Keep it on your backend; it authenticates your application and doesn't sign transactions. | Scope | Allows | | --- | --- | diff --git a/src/pages/docs/routes/fx.mdx b/src/pages/docs/routes/fx.mdx index e314d1f98..1f33f3589 100644 --- a/src/pages/docs/routes/fx.mdx +++ b/src/pages/docs/routes/fx.mdx @@ -45,7 +45,7 @@ Before you start, have: - The route's pool address. Tempo provides it when it enables your access. - A signer for a listed account that holds the source stablecoin and a fee token. -The [FX swap example](/docs/routes/examples/fx) runs every step from a terminal. +Every step also runs from a terminal with curl and jq, as the [FX swap example](/docs/routes/examples/fx) shows. ## Swap one stablecoin for another @@ -70,7 +70,11 @@ Without a saved list, the request returns `400` with code `propamm_invalid`. If Send the account as both `sender` and `recipient`. Amounts are in base units. Routes quotes under your organization's list, so the request names no pool or list. - + Show the source amount, the destination amount, and `destinationAmountMin`. Create the transfer before `quote.expiresAt`. @@ -78,7 +82,25 @@ Show the source amount, the destination amount, and `destinationAmountMin`. Crea Send the same inputs with an [idempotency key](/docs/routes/api-setup#set-idempotency-keys). - +```bash [Terminal] +curl 'https://api.tempo.xyz/v1/routes/transfers' \ + --request POST \ + --header "tempo-api-key: $TEMPO_API_KEY" \ + --header 'idempotency-key: ' \ + --header 'Content-Type: application/json' \ + --data '{ + "sourceChain": "tempo", + "sourceToken": "0x20c0000000000000000000006a37da5c996874be", + "destinationChain": "tempo", + "destinationToken": "0x20c0000000000000000000001621e21f71cf12fb", + "sender": "", + "recipient": "", + "amount": "1000000", + "mode": "exactSource" + }' +``` + +This request swaps 1 OUSD for EURC.e from one account to itself. Save the transfer `id` and its `action`. For an exact-source swap, `action.calls` holds two calls. The first approves the source stablecoin. The second swaps it. @@ -112,20 +134,22 @@ A list has one admin account. The admin adds or removes up to 50 accounts in one Send the list's admin account as `sender`. Name the pair by its two token addresses, in either order. -```json -{ - "sender": "", - "action": { - "type": "setMembers", - "tokens": ["", ""], - "accounts": [""], - "allowed": true - } -} +```bash [Terminal] +curl 'https://api.tempo.xyz/v1/prop-amms//access/prepare' \ + --request POST \ + --header "tempo-api-key: $TEMPO_API_KEY" \ + --header 'Content-Type: application/json' \ + --data '{ + "sender": "", + "action": { + "type": "setMembers", + "tokens": ["", ""], + "accounts": [""], + "allowed": true + } + }' ``` - - The API applies the change to your organization's saved list. It simulates the change and returns an unsigned `transaction`. `effect` names the resolved pair index and sponsor ID, such as `List 1 account(s) on pair 0 under sponsor 5.` To remove accounts, send the same request with `"allowed": false`. @@ -170,4 +194,4 @@ A refused quote returns `404` with code `quote_not_available`. Its `details` nam | `recipient` | The recipient differs from the sender. | Set `recipient` to the `sender` address. | | `amount` | The pool can't fill that size now. | Lower the amount or retry later. | -Continue with [Track delivery](/docs/routes/delivery) for every transfer status. +An FX transfer reports the same [transfer statuses](/docs/routes/delivery) as other Routes transfers, such as `action-required` when it needs attention. diff --git a/src/pages/docs/routes/test.mdx b/src/pages/docs/routes/test.mdx index 8a58e3044..de7f4b11a 100644 --- a/src/pages/docs/routes/test.mdx +++ b/src/pages/docs/routes/test.mdx @@ -20,7 +20,7 @@ Run a route end to end in your browser and watch each [Routes API](/docs/routes) ## Before you run it - **Real funds.** The demo moves real funds on live networks. Start with a small amount. -- **API key.** Creating a route needs a Routes API key from [Tempo Console](https://console.tempo.xyz/?to=/:org/api-keys) with the `routes:read`, `routes:write`, and `data:read` [scopes](/docs/routes/api-setup#routes-api-key-scopes). +- **API key.** Creating a route needs a Routes API key from [Tempo Console](https://tempo.xyz/console?to=/:org/api-keys) with the `routes:read`, `routes:write`, and `data:read` [scopes](/docs/routes/api-setup#routes-api-key-scopes). - **Key storage.** The key stays only in this tab's memory. - **No key.** Without a key, the demo quotes from a demo organization that can quote but not create routes. Its quotes show the subsidized 1:1 terms an approved organization receives. diff --git a/vercel.json b/vercel.json index cfb0435e3..4af94d122 100644 --- a/vercel.json +++ b/vercel.json @@ -523,12 +523,12 @@ }, { "source": "/login", - "destination": "https://console.tempo.xyz", + "destination": "https://tempo.xyz/console", "permanent": false }, { "source": "/developers/login", - "destination": "https://console.tempo.xyz", + "destination": "https://tempo.xyz/console", "permanent": false }, { From f9de8bef044bba843d4f522f5b36db1857a69a87 Mon Sep 17 00:00:00 2001 From: Jason Date: Sat, 14 Nov 2026 00:22:06 +0100 Subject: [PATCH 4/4] docs(routes): tell readers to use their listed account in the FX quote --- src/pages/docs/routes/fx.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/docs/routes/fx.mdx b/src/pages/docs/routes/fx.mdx index 1f33f3589..9515291ee 100644 --- a/src/pages/docs/routes/fx.mdx +++ b/src/pages/docs/routes/fx.mdx @@ -68,7 +68,7 @@ Without a saved list, the request returns `400` with code `propamm_invalid`. If ### Get a quote -Send the account as both `sender` and `recipient`. Amounts are in base units. Routes quotes under your organization's list, so the request names no pool or list. +Send your listed account as both `sender` and `recipient`, in place of the example's `0x2222…2222`. Amounts are in base units. Routes quotes under your organization's list, so the request names no pool or list.