Relic App Transfers
Send supported relic currencies from your app's funded treasury user to a target user without minting balance.
Local portal docs
Use this flow when your service needs to send a relic currency to one of your users from your own funded treasury account.
Relics is a configurable currency system. Approved businesses can create their own currency branches, decide whether each branch supports buy, sell, withdrawal, exchange, or closed-loop use, and settle value instantly through the relic ledger. A branch can be fully off-chain, or it can be paired with an on-chain asset strategy while still using relic balances for fast account settlement, treasury payouts, escrow, and payment authorization.
Use app transfers for treasury-funded movement: rewards, partner payouts, account credits, B2B settlement, customer service adjustments, or issuer-controlled distribution.
This API does not mint currency. Every transfer spends from the treasury user configured for your app.
Summary
- Create an app in the Developers portal and keep its
clientIdandclientSecreton your server. - Ask TCM to enable relic app transfers for that client and map it to your treasury user.
- Keep your treasury user funded in the currencies your app will distribute.
- Call the public relic app-transfer endpoint whenever you want to send currency to a user.
Endpoint
POST https://www.thecrimsonmarket.com/relics/api/relics/app-transferUse your actual relic service base URL for the environment you are targeting.
List Available Currencies
Fetch enabled currencies before presenting transfer or treasury options:
GET https://www.thecrimsonmarket.com/relics/api/relics/currenciesUse data.currencies[].key as the relicType value. Capability flags describe the currency globally; your OAuth client must still be enabled for that currency before it can transfer it.
Authentication
Send your Developers app credentials in the request body:
clientIdclientSecret
Keep clientSecret server-side only.
Request fields
Required:
clientIdclientSecrettoUserIdamountrelicTypetransactionId
Optional:
reason
Example request
{
"clientId": "tcm_xxx",
"clientSecret": "your-oauth-client-secret",
"toUserId": "64c672ff9e49204d7f5dd5ca",
"amount": 500,
"relicType": "charades-coin",
"reason": "Reward payout",
"transactionId": "reward-2026-05-19-0001"
}Behavior
- The app credentials are validated before the transfer runs.
- The transfer always spends from the treasury user configured for your app.
- Your app can send only the currencies that have been enabled for that client.
- If the treasury user does not have enough balance, the request fails.
transactionIdis idempotent. Repeating the same request returns the already-processed transfer result instead of charging again.
Example success response
{
"success": true,
"data": {
"type": "app_transfer",
"relicType": "charades-coin",
"clientId": "tcm_xxx",
"treasuryUserId": "partner-treasury-user",
"alreadyProcessed": false,
"transactionId": "app_tx_out_example",
"fromUser": {
"userId": "partner-treasury-user",
"previousAmount": 10000,
"newAmount": 9500,
"transferred": -500
},
"toUser": {
"userId": "64c672ff9e49204d7f5dd5ca",
"previousAmount": 0,
"newAmount": 500,
"received": 500
},
"reason": "Reward payout",
"timestamp": "2026-05-19T12:00:00.000Z"
}
}Common failure cases
Invalid credentials
{
"success": false,
"error": "Invalid client credentials"
}Currency not enabled for your app
{
"success": false,
"error": "This client is not allowed to transfer that currency"
}Treasury balance too low
{
"success": false,
"error": "Treasury user has insufficient Charades Coin",
"data": {
"treasuryUserId": "partner-treasury-user",
"currentBalance": 250,
"requiredAmount": 500,
"shortfall": 250
}
}Operational notes
- This flow is for server-to-server use only.
- Do not expose
clientSecretin a browser or mobile client. transactionIdshould be unique per payout attempt from your system.- This flow transfers existing balance. It is not a purchase flow and it is not a wallet withdrawal flow.
- If you need this enabled for your app, contact TCM with your
clientId, treasury user id, and the currencies you need to distribute.
Delegated User Transfers (v2)
Use this flow when your app moves relics on behalf of the currently signed-in user — for example one user tipping another. The sender is always the user, never a treasury, and no relics-side setup is required.
How it works
- Create a service client in the Developers portal (service-clients tab) with the
service.relicsaudience and therelics:delegated-transferscope. - On your server, mint a client_credentials token from Portal with
resource=service.relics. - Call the endpoint with the user's own bearer token plus your service token:
POST https://www.thecrimsonmarket.com/relics/api/relics/delegated-transferAuthorization: Bearer <signed-in-user-token>
x-service-token: <portal-service-token>
Content-Type: application/json{
"toUserId": "64c672ff9e49204d7f5dd5ca",
"amount": 500,
"relicType": "charades-coin",
"reason": "Community alert tip",
"transactionId": "community-alert-tip-0001"
}Behavior
- The sender is resolved from the user's bearer token. Your app never names the debit side and cannot move funds without the user's live token.
- Any enabled currency is transferable by default. Your client is auto-registered with relics on first use; TCM admins can restrict currencies, cap amounts, or disable your integration from there.
transactionIdis idempotent per client — retrying the same id returns the original result.- Failures carry a machine-readable
code, e.g.service_token_invalid,invalid_audience,insufficient_scope,client_disabled,relic_type_not_allowed,amount_exceeds_limit,currency_not_enabled.
Deprecated: body-credential delegated transfers (v1)
Older integrations send clientId + clientSecret in the request body instead of x-service-token. That variant still works but is deprecated: it requires TCM to hand-provision your client inside relics with your currencies explicitly listed, and it puts your secret in every request body. New integrations should use the service-token flow above.
Buy Widget
Use the buy widget when you want to embed a purchase flow for a single currency inside your own service.
Widget URL
https://www.thecrimsonmarket.com/relics/widget/buy?relicType=charades-coinOptional query parameters:
relicTyperequired currency key to lock the widget to one currencyparentOriginoptional expected parent origin for stricter postMessage validation
Example:
https://www.thecrimsonmarket.com/relics/widget/buy?relicType=charades-coin&parentOrigin=https%3A%2F%2Fpartner.example.comWhat the widget does
- shows the configured currency only
- shows the signed-in user's current public balance for that currency
- lets the user enter a custom amount
- opens the embedded Stripe checkout for that currency
Authentication model
Same-domain embeds can rely on the normal shared auth cookie.
Cross-domain embeds should use an iframe token handshake instead of relying on third-party cookies.
Child iframe behavior
The widget sends this message when it is ready:
{
"type": "IFRAME_READY",
"widget": "relics-buy"
}The parent should respond with:
{
"type": "TOKEN_DELIVERY",
"token": "user-auth-token"
}The widget then uses that token as a Bearer token for authenticated relic requests.
Example embed
<iframe
src="https://www.thecrimsonmarket.com/relics/widget/buy?relicType=charades-coin&parentOrigin=https%3A%2F%2Fpartner.example.com"
width="420"
height="760"
style="border:0;"
allow="payment *"
></iframe>Example parent handshake
<script>
window.addEventListener('message', (event) => {
if (event.origin !== 'https://www.thecrimsonmarket.com') return;
if (event.data?.type !== 'IFRAME_READY') return;
const iframe = document.getElementById('relic-buy-widget');
iframe?.contentWindow?.postMessage({
type: 'TOKEN_DELIVERY',
token: window.mySignedInUserToken
}, 'https://www.thecrimsonmarket.com');
});
</script>Notes
- Keep the user token server-issued and scoped to the signed-in user.
- Do not expose app
clientSecretvalues in the widget or browser. - The widget is a purchase surface only. It does not mint arbitrary balances and it does not replace treasury-funded app transfers.