Skip to main content

API Agents

API agents let a wallet owner create durable bot credentials without sending the wallet private key to Senticore. The owner signs an authorization message with the wallet, and the API returns an agent plus optional machine credentials.

Use this flow for server-side bots, FIX sessions, market-maker tools, and integration tests that should not ask MetaMask to sign every order.

Credential types

Credential typeReturned secretsUse
HMACapiKeyId, apiSecret, apiPassphraseFIX Logon, SC-Auth-Version: 2, durable bot/API access
Ed25519apiKeyId, public key onlyHTTP machine auth where Ed25519 is enabled

For FIX, request HMAC credentials. Ed25519 credentials are HTTP-only and are not accepted by the current FIX Logon path.

One-call HMAC agent creation

POST /api/agents/create can create the agent and issue the HMAC credential in the same response. Set issueInitialCredential to true.

POST /api/agents/create
Content-Type: application/json
{
"authorization": {
"domain": "app.sentico-labs.xyz",
"appName": "SentiPredict",
"scheme": "senticore_typed_v2",
"environment": "Arbitrum One",
"chainId": 42161,
"userWalletAddress": "0xOwnerWallet",
"agentPublicKey": "0xApiAgentAddress",
"label": "Market maker bot",
"agentType": "api_agent",
"scopes": ["read", "trade", "cancel", "quote", "drop_copy"],
"policy": {},
"expiresAt": 1790000000000,
"authorizationNonce": "7c4a0e4a5d43c4d8c9e2a55b",
"issuedAt": 1760000000000,
"signature": "0x..."
},
"issueInitialCredential": true
}

Successful responses include the agent, the credential id, and the HMAC secret material. Store apiSecret and apiPassphrase immediately; they are shown only once.

{
"ok": true,
"agent": {
"id": "agt_...",
"agentType": "api_agent",
"scopes": ["read", "trade", "cancel", "quote", "drop_copy"]
},
"credential": {
"apiKeyId": "spk_...",
"authScheme": "hmac_v2"
},
"apiSecret": "sps_...",
"apiPassphrase": "spp_...",
"warning": "Legacy HMAC secret and passphrase are shown only once..."
}

Authorization message

The wallet signs the exact text below with the owner wallet. Browser wallets use the normal message signing flow, for example MetaMask personal_sign.

SentiCore Typed Agent Authorization
type:senticore.agent_authorization
version:2
app:SentiPredict
domain:app.sentico-labs.xyz
environment:Arbitrum One
chainId:42161
wallet:0xownerwallet
agentPublicKey:0xapiagentaddress
label:Market maker bot
agentType:api_agent
scopes:read,trade,cancel,quote,drop_copy
policy:{}
nonce:7c4a0e4a5d43c4d8c9e2a55b
issuedAt:1760000000000
expiresAt:1790000000000

If the authorization is bound to a specific trading account, include this line after wallet:

accountId:0x...

appName, domain, environment, and chainId must match the deployed runtime configuration. In the current beta frontend the app name is SentiPredict.

TypeScript example

This example uses an owner wallet to authorize a newly generated API-agent address. It requests HMAC credentials by setting issueInitialCredential: true.

import { BrowserProvider, Wallet, hexlify, randomBytes } from "ethers";

const API_BASE_URL = "https://api.sentico-labs.xyz";
const owner = "0xOwnerWallet";
const provider = new BrowserProvider(window.ethereum);
const signer = await provider.getSigner();

const now = Date.now();
const agent = Wallet.createRandom();
const authorization = {
domain: "app.sentico-labs.xyz",
appName: "SentiPredict",
scheme: "senticore_typed_v2",
environment: "Arbitrum One",
chainId: 42161,
userWalletAddress: owner.toLowerCase(),
agentPublicKey: agent.address.toLowerCase(),
label: "Market maker bot",
agentType: "api_agent",
scopes: ["read", "trade", "cancel", "quote", "drop_copy"],
policy: {},
expiresAt: now + 30 * 24 * 60 * 60 * 1000,
authorizationNonce: hexlify(randomBytes(12)).slice(2),
issuedAt: now
};

const message = [
"SentiCore Typed Agent Authorization",
"type:senticore.agent_authorization",
"version:2",
`app:${authorization.appName}`,
`domain:${authorization.domain}`,
`environment:${authorization.environment}`,
`chainId:${authorization.chainId}`,
`wallet:${authorization.userWalletAddress}`,
`agentPublicKey:${authorization.agentPublicKey}`,
`label:${authorization.label}`,
`agentType:${authorization.agentType}`,
`scopes:${authorization.scopes.join(",")}`,
`policy:${JSON.stringify(authorization.policy)}`,
`nonce:${authorization.authorizationNonce}`,
`issuedAt:${authorization.issuedAt}`,
`expiresAt:${authorization.expiresAt}`
].join("\n");

const signature = await signer.signMessage(message);

const response = await fetch(`${API_BASE_URL}/api/agents/create`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
authorization: { ...authorization, signature },
issueInitialCredential: true
})
});

if (!response.ok) {
throw new Error(await response.text());
}

const created = await response.json();
console.log({
agentId: created.agent.id,
apiKeyId: created.credential.apiKeyId,
apiSecret: created.apiSecret,
apiPassphrase: created.apiPassphrase,
agentPrivateKey: agent.privateKey
});

The generated agentPrivateKey belongs to the API agent, not to the owner wallet. Keep it with the bot if the bot will sign delegated trading actions. Never export or upload the owner wallet private key.

Two-step credential creation

If the agent already exists, create or rotate a credential separately:

POST /api/agents/{agentId}/credentials/create
Content-Type: application/json
{
"walletAuth": {
"domain": "app.sentico-labs.xyz",
"appName": "SentiPredict",
"scheme": "senticore_typed_v2",
"environment": "Arbitrum One",
"chainId": 42161,
"walletAddress": "0xOwnerWallet",
"nonce": "0f2a9d64a2f40efb6e0f567c",
"issuedAt": 1760000000000,
"expiresAt": 1760000300000,
"signature": "0x..."
},
"credentialKind": "hmac",
"cancelOnDisconnect": true,
"defaultStpMode": "cancel_maker"
}

The wallet signs this admin message:

SentiCore Typed Wallet Admin Authorization
type:senticore.wallet_admin
version:2
app:SentiPredict
domain:app.sentico-labs.xyz
environment:Arbitrum One
chainId:42161
wallet:0xownerwallet
action:create_agent_credential
targetAgentId:agt_...
nonce:0f2a9d64a2f40efb6e0f567c
issuedAt:1760000000000
expiresAt:1760000300000

To create an Ed25519 credential instead, send credentialKind: "ed25519" plus publicKeyAlgorithm: "ed25519" and publicKey. Do not use Ed25519 for FIX.

Using HMAC credentials

For HTTP machine auth:

SC-Auth-Version: 2
SC-Key: <apiKeyId>
SC-Nonce: <monotonic nonce>
SC-Timestamp: <unix ms>
SC-Passphrase: <apiPassphrase>
SC-Signature: <hmac signature>

For FIX Logon:

FIX tagValue
553apiKeyId
554apiSecret:apiPassphrase

For low-latency order-entry, a separate order-entry entitlement may still be required:

X-Senticore-Order-Entry-Key: <order-entry-api-key>

That key is provisioned through builder/MM onboarding and controls the order-entry lane and rate tier. The signed action inside the batch still needs valid account authorization and risk checks.

Security notes

  • Show apiSecret and apiPassphrase only once.
  • Store only hashes/encrypted secrets server-side; never log the raw response.
  • Use policy, IP allowlists, expiry, and narrow scopes for production bots.
  • Revoke compromised agents with POST /api/agents/revoke.
  • Rotate credentials before sharing access with a new bot or operator.