Skip to main content

API Key + HMAC

Use this page for Auto integrations on /v2/auto/*. If you still need to choose between API key and x402 models, start with Auto Overview.

Base URL

https://api.elfa.ai/v2/auto

Developer Portal Setup

Auto is enabled per API key in the Developer Portal. Use this flow:

  1. Open https://dev.elfa.ai/, select your API key, and open the Auto tab.
  2. Click Sign in to Enable Auto.
  3. You will be redirected to a Privy login screen. If you already have an ELFA account, sign in with the same login method so your existing plans and queries are linked.
  4. After successful sign-in, the Auto card updates to Auto is enabled and shows your API key details.
  5. Copy the initial HMAC secret when it is shown. The initial secret display is one-time.
  6. If needed, click Regenerate to rotate the HMAC secret.
HMAC Secret Rotation

Regenerating the HMAC secret can break existing clients until they are updated to sign requests with the new secret.

  1. For exchange-backed execution (for example Hyperliquid), open the ELFA app from Exchange Connections, complete setup there using the same account, then return and click Verify connection.

Expected UI progression: Sign in to Enable Auto -> Auto is enabled (API key + HMAC section) -> Exchange Connections and Quick Start visible.

Credential Hygiene (Required)

Never Print Secrets

Never print or log ELFA_API_KEY, ELFA_HMAC_SECRET, x-elfa-signature, or full auth headers.

Do not use debug commands like echo $ELFA_API_KEY, printenv, or console.log(process.env).

If you must debug auth, log only redacted metadata (for example request path and timestamp), not secret values.

Execution Prerequisite (Order Actions)

market_order and limit_order actions require an active exchange connection on the linked user for the venue the action targets (hyperliquid or gmx), managed via /v2/auto/exchanges.

If no exchange is connected, a trade-action query may still be created, but order execution fails at runtime when the trigger fires.

Use this quick-start request pattern for POST /v2/auto/queries:

API_KEY="your_api_key"
HMAC_SECRET="your_hmac_secret"
TIMESTAMP=$(date +%s)
BODY='{"query":{"conditions":{"AND":[{"source":"price","method":"current","args":{"symbol":"BTC"},"operator":">","value":80000}]},"actions":[{"stepId":"step_1","type":"notify","params":{"message":"BTC crossed 80k"}}],"expiresIn":"24h"}}'

# Sign requests with HMAC-SHA256.
# Important: sign mounted path "/queries" (NOT "/v2/auto/queries").
SIGNATURE=$(echo -n "${TIMESTAMP}POST/queries${BODY}" | \
openssl dgst -sha256 -hmac "${HMAC_SECRET}" -hex | awk '{print $2}')

curl -X POST https://api.elfa.ai/v2/auto/queries \
-H "x-elfa-api-key: ${API_KEY}" \
-H "x-elfa-signature: ${SIGNATURE}" \
-H "x-elfa-timestamp: ${TIMESTAMP}" \
-H "Content-Type: application/json" \
-d "${BODY}"
Legacy Dashboard Snippet

If you still see /v2/athena/* in a dashboard quick-start snippet, treat it as outdated and use /v2/auto/* from this page.

Required Headers

All /v2/auto/* routes require:

  • x-elfa-api-key

Trade-action mutations also require:

  • x-elfa-signature
  • x-elfa-timestamp

Notification-only mutations skip HMAC — see below.

HMAC Bypass for Notification-Only Mutations

Mutations whose EQL action is a pure notification skip the HMAC requirement. This lets agents onboard without first generating an HMAC secret. Trade execution and exchange linking continue to require HMAC unconditionally.

Notification action types (HMAC bypassed):

  • notify
  • telegram_bot
  • webhook
  • llm whose params.callback.action.type is one of the above

Trade action types (HMAC required):

  • market_order
  • limit_order
  • llm whose params.callback.action.type is market_order or limit_order

Decision is per-route:

RouteDecision input
POST /queries, POST /queries/draftsRequest body's query.actions[*].type
POST /queries/drafts/:id/convertStored draft's actions
POST /queries/:id/cancel, DELETE /queries/:idStored query's actions

If the lookup fails or the action type is unknown, HMAC is enforced (fail-safe). Unknown action types added in future API versions default to requiring HMAC, so always-signing clients keep working.

POST /chat is fully ungated regardless of content because it produces drafts only — activation flows through convert, which is still gated when the draft is trade-flavoured.

POST /exchanges and DELETE /exchanges/:exchange always require HMAC — linking an exchange is the gateway to trade execution.

Always-Signing Clients

If your client signs every mutation, you do not need to opt into the bypass. Signed requests are accepted on every route. The bypass is purely an optimization for clients that want to skip the HMAC setup step.

HMAC Signing

Headers:

x-elfa-signature: <hex_hmac_sha256>
x-elfa-timestamp: <unix_seconds>

Payload format:

timestamp + method + path + body

Where:

  • timestamp: unix seconds
  • method: uppercase HTTP method
  • path: route path verified by Auto router
  • body: exact JSON string body, or empty string

Replay window: +/-30 seconds.

Important mounted-router detail:

  • Request URL may be /v2/auto/queries
  • Signature path must be /queries (the mounted router path)

Using /v2/auto/queries in the signed payload will fail verification.

URL Path vs Signature Path

HTTP MethodRequest URL pathSignature path
POST/v2/auto/queries/queries
DELETE/v2/auto/queries/{id}/queries/{id}
POST/v2/auto/chat/chat
POST/v2/auto/exchanges/exchanges
DELETE/v2/auto/exchanges/{exchange}/exchanges/{exchange}

Concrete Signing Example

Example target endpoint: POST /v2/auto/queries

import crypto from "crypto";

const hmacSecret = process.env.ELFA_HMAC_SECRET!;
const timestamp = Math.floor(Date.now() / 1000).toString();
const method = "POST";
const path = "/queries"; // mounted path (NOT /v2/auto/queries)

const bodyObj = {
title: "BTC Alert",
description: "Notify when BTC trades above the 100k level so I can review the breakout setup.",
query: {
conditions: {
AND: [
{
source: "price",
method: "current",
args: { symbol: "BTC" },
operator: ">",
value: 100000,
},
],
},
actions: [
{
stepId: "step_1",
type: "notify",
params: { message: "BTC crossed target" },
},
],
expiresIn: "24h",
},
};

const body = JSON.stringify(bodyObj);
const payload = `${timestamp}${method}${path}${body}`;
const signature = crypto
.createHmac("sha256", hmacSecret)
.update(payload)
.digest("hex");

console.log({ timestamp, path }); // Avoid logging secrets/signatures/bodies in production logs.

Then send:

POST /v2/auto/queries
x-elfa-api-key: <api_key>
x-elfa-timestamp: <timestamp>
x-elfa-signature: <signature>
content-type: application/json

<body exactly as signed>

No-body example (DELETE /v2/auto/queries/{id}): sign with path = "/queries/{id}" and body = "".

Exchange Mutation Signing Examples

POST /v2/auto/exchanges (sign with path = "/exchanges"):

import crypto from "crypto";

const hmacSecret = process.env.ELFA_HMAC_SECRET!;
const timestamp = Math.floor(Date.now() / 1000).toString();
const method = "POST";
const path = "/exchanges";
const body = JSON.stringify({
exchange: "hyperliquid",
credentialType: "agent_wallet",
metadata: {
masterAddress: "0x1111111111111111111111111111111111111111",
agentAddress: "0x2222222222222222222222222222222222222222",
},
});

const signature = crypto
.createHmac("sha256", hmacSecret)
.update(`${timestamp}${method}${path}${body}`)
.digest("hex");

DELETE /v2/auto/exchanges/{exchange} (sign with path = "/exchanges/{exchange}", empty body):

import crypto from "crypto";

const hmacSecret = process.env.ELFA_HMAC_SECRET!;
const timestamp = Math.floor(Date.now() / 1000).toString();
const method = "DELETE";
const exchange = "hyperliquid";
const path = `/exchanges/${exchange}`;
const body = "";

const signature = crypto
.createHmac("sha256", hmacSecret)
.update(`${timestamp}${method}${path}${body}`)
.digest("hex");

Enablement Requirements

API keys must be Auto-enabled and linked to a user identity in the Developer Portal. If not enabled/linked, /v2/auto/* returns 403.

Endpoint Matrix

Mounted endpoints below are the routes inside /v2/auto.

"Conditional" below means HMAC is required only when the action is trade-flavoured (market_order, limit_order, or llm callback to those). Notification-only actions skip HMAC. See HMAC Bypass for Notification-Only Mutations for the full decision rule.

Queries and Drafts

EndpointHMAC
GET /queriesNo
GET /queries/:idNo
GET /queries/:id/evaluationsNo
GET /queries/:id/streamNo
GET /queries/:id/sessionsNo
GET /queries/:id/sessions/:sessionIdNo
POST /queries/validateNo
POST /queries/previewNo
POST /queriesConditional (body)
POST /queries/:id/cancelConditional (stored query)
DELETE /queries/:idConditional (stored query)
GET /queries/draftsNo
GET /queries/drafts/:idNo
POST /queries/draftsConditional (body)
DELETE /queries/drafts/:idNo
POST /queries/drafts/:id/previewNo
POST /queries/drafts/:id/convertConditional (stored draft)

Executions

EndpointHMAC
GET /executionsNo
GET /executions/:idNo

Chat

EndpointHMAC
POST /chatNo

Exchange Connections

EndpointHMAC
GET /exchangesNo
POST /exchangesYes
DELETE /exchanges/:exchangeYes

Common Errors

  • 401: missing/invalid key, signature, timestamp, or clock skew
  • 403: Auto not enabled for the API key or no linked user
  • 422: validation failure (query content)