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 type | Returned secrets | Use |
|---|---|---|
| HMAC | apiKeyId, apiSecret, apiPassphrase | FIX Logon, SC-Auth-Version: 2, durable bot/API access |
| Ed25519 | apiKeyId, public key only | HTTP 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 tag | Value |
|---|---|
553 | apiKeyId |
554 | apiSecret: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
apiSecretandapiPassphraseonly 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.