The Absurdia API uses agent tokens to authenticate requests. You can view and manage your agents in Visage. You can use restricted agent tokens for granular permissions.
Your agents are assigned many privileges, so be sure to keep them secure! Do not share your agents' credentials in publicly accessible areas such as GitHub, client-side code, and so forth.
Authentication to the API is performed via HTTP bearer auth. Provide your agent token as the bearer value using the header Authorization: Bearer {{token}}.
All API requests must be made over HTTPS. Calls made over plain HTTP will fail. API requests without authentication will also fail.
All requests that may cause a change of state, such as updates, require a signature using the agent's signature key. In the documentation, endpoints that require a signature are labeled with signature required.
Before you can sign requests, you need to create a new agent in your dashboard and retrieve the signature key which will be generated and visible only once. The signature uses the ed25519 algorithm.
The signed request is a timestamp in either milliseconds or microseconds + . + the string-ified JSON body. The signature must be URL-safe base64 encoded with no padding, and added in the header Abs-Signature. The header includes both the timestamp, prefixed by t=, and the signature, prefixed by s=.
Abs-Signature: t=1658953321960,s=aLtqazkwxF7I-SkPePoCEOyrYaYuXIYHfN21tTw9YdaT61jX9_XxPBr2e-qeTzB-VPJn6lZcHCGLQN1p4XvyCQ
For example, the timestamp 1658953321960 with the base request
{
"id": "randomid123",
"name": "a new name"
}
must joined together to form the string
1658953321960.{"id":"randomid123","name":"a new name"}
for the signature. The signature is performed on the UTF-8 byte representation of that string.
Refer to our code snippets to see how to safely generate those signatures in different programming languages. Feel free to simply copy-paste the functions.
curl https://api.absurdia.markets/v1/symbols
--compressed
-H "Accept-Encoding: gzip"
-H "Accept: application/json"
-H "Authorization: Bearer {{token}}"
-H "Abs-Signature: t=...,s=..."
import time, json, base64
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
# Prepare the ed25519 private key using the signature key
key = Ed25519PrivateKey.from_private_bytes(base64.b64decode(SIGNATURE_KEY))
# Utility function to properly string-ify a JSON
dump = lambda x: json.dumps(x, separators=(",", ":"))
def sign(payload: dict) -> dict:
# Add a UNIX timestamp in milliseconds to the payload
timestamp = int(time.time_ns() / 1000)
# Dump the dict to a JSON with no whitespace in the separators
# then encode the string to get raw bytes
to_sign = f"{timestamp}.{dump(payload)}".encode()
# Generate the signature
sig_raw = key.sign(to_sign)
# Convert the signature into a base64-encoded string with no padding
signature = base64.urlsafe_b64encode(sig_raw).replace(b'=', b'').decode()
# Format the header value
return f"t={timestamp},s={signature}"
header = { "Abs-Signature": sign(payload) }
const ed = require('@noble/ed25519');
// Prepare the ed25519 private key (see utility below)
const key = base64ToHex(SIGNATURE_KEY);
async function sign(payload) {
// Add a UNIX timestamp in milliseconds
const timestamp = Date.now() * 1000
// Dump the timestamp and payload to a JSON string
const to_sign = `${timestamp}.${JSON.stringify(payload)}`
// Encode the string to get raw bytes
const encoder = new TextEncoder();
const to_sign_bytes = encoder.encode(to_sign);
// Generate the signature
const sig_raw = await ed.sign(to_sign_bytes, key);
// Convert the signature into a base64-encoded string
const signature = safe_b64encode(sig_raw);
// Format the header value
return `t= ${timestamp}, s=${signature}`
}
payload = { "Abs-Signature": await sign(payload) };
/*
Utilities to avoid adding dependencies in the project
*/
// Function to convert a base64 to a hex encoded string
function base64ToHex(str) {
const raw = atob(str);
let result = '';
for (let i = 0; i < raw.length; i++) {
const hex = raw.charCodeAt(i).toString(16);
result += (hex.length === 2 ? hex : '0' + hex);
}
return result.toUpperCase();
}
// Utility fn to get a URL-safe encoded base64 with no padding
function safe_b64encode(bytes) {
return btoa(
ed.utils.bytesToHex(bytes)
.match(/w{2}/g)
.map(function(a) {
return String.fromCharCode(parseInt(a, 16));
})
.join("")
)
.replace(/+/g, '-')
.replace(///g, '_')
.replace('=', '');
}