curl --request POST \
--url https://api.arcus.xyz/v1/revokeApiKey \
--header 'Content-Type: application/json' \
--data '
{
"address": "0x742d35cc6634c0532925a3b844bc9e7595f2bd18",
"publicKey": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
"apiWalletName": "Arcus",
"signature": {
"r": "0x69e7308419769592ec8363d002e3077e803e403b4e1920fc30e485b63d76e2a3",
"s": "0x0980d0742c9e65037af9d54e559704ae2c2285f3112a9d89544191a740f103cf",
"v": "0x1c"
},
"nonce": "a1b2c3d4-5f60-7182-93a4-b5c6d7e8f901"
}
'import requests
url = "https://api.arcus.xyz/v1/revokeApiKey"
payload = {
"address": "0x742d35cc6634c0532925a3b844bc9e7595f2bd18",
"publicKey": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
"apiWalletName": "Arcus",
"signature": {
"r": "0x69e7308419769592ec8363d002e3077e803e403b4e1920fc30e485b63d76e2a3",
"s": "0x0980d0742c9e65037af9d54e559704ae2c2285f3112a9d89544191a740f103cf",
"v": "0x1c"
},
"nonce": "a1b2c3d4-5f60-7182-93a4-b5c6d7e8f901"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
address: '0x742d35cc6634c0532925a3b844bc9e7595f2bd18',
publicKey: 'a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2',
apiWalletName: 'Arcus',
signature: {
r: '0x69e7308419769592ec8363d002e3077e803e403b4e1920fc30e485b63d76e2a3',
s: '0x0980d0742c9e65037af9d54e559704ae2c2285f3112a9d89544191a740f103cf',
v: '0x1c'
},
nonce: 'a1b2c3d4-5f60-7182-93a4-b5c6d7e8f901'
})
};
fetch('https://api.arcus.xyz/v1/revokeApiKey', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.arcus.xyz/v1/revokeApiKey",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'address' => '0x742d35cc6634c0532925a3b844bc9e7595f2bd18',
'publicKey' => 'a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2',
'apiWalletName' => 'Arcus',
'signature' => [
'r' => '0x69e7308419769592ec8363d002e3077e803e403b4e1920fc30e485b63d76e2a3',
's' => '0x0980d0742c9e65037af9d54e559704ae2c2285f3112a9d89544191a740f103cf',
'v' => '0x1c'
],
'nonce' => 'a1b2c3d4-5f60-7182-93a4-b5c6d7e8f901'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.arcus.xyz/v1/revokeApiKey"
payload := strings.NewReader("{\n \"address\": \"0x742d35cc6634c0532925a3b844bc9e7595f2bd18\",\n \"publicKey\": \"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2\",\n \"apiWalletName\": \"Arcus\",\n \"signature\": {\n \"r\": \"0x69e7308419769592ec8363d002e3077e803e403b4e1920fc30e485b63d76e2a3\",\n \"s\": \"0x0980d0742c9e65037af9d54e559704ae2c2285f3112a9d89544191a740f103cf\",\n \"v\": \"0x1c\"\n },\n \"nonce\": \"a1b2c3d4-5f60-7182-93a4-b5c6d7e8f901\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.arcus.xyz/v1/revokeApiKey")
.header("Content-Type", "application/json")
.body("{\n \"address\": \"0x742d35cc6634c0532925a3b844bc9e7595f2bd18\",\n \"publicKey\": \"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2\",\n \"apiWalletName\": \"Arcus\",\n \"signature\": {\n \"r\": \"0x69e7308419769592ec8363d002e3077e803e403b4e1920fc30e485b63d76e2a3\",\n \"s\": \"0x0980d0742c9e65037af9d54e559704ae2c2285f3112a9d89544191a740f103cf\",\n \"v\": \"0x1c\"\n },\n \"nonce\": \"a1b2c3d4-5f60-7182-93a4-b5c6d7e8f901\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.arcus.xyz/v1/revokeApiKey")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"address\": \"0x742d35cc6634c0532925a3b844bc9e7595f2bd18\",\n \"publicKey\": \"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2\",\n \"apiWalletName\": \"Arcus\",\n \"signature\": {\n \"r\": \"0x69e7308419769592ec8363d002e3077e803e403b4e1920fc30e485b63d76e2a3\",\n \"s\": \"0x0980d0742c9e65037af9d54e559704ae2c2285f3112a9d89544191a740f103cf\",\n \"v\": \"0x1c\"\n },\n \"nonce\": \"a1b2c3d4-5f60-7182-93a4-b5c6d7e8f901\"\n}"
response = http.request(request)
puts response.read_body{
"apiKey": "<string>",
"address": "<string>",
"revokedAt": 123,
"allSubaccounts": true,
"accountIndex": 4
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}{
"error": "rate limited",
"reason": "account_empty",
"retryAfterMs": 850,
"clientId": "my-order-42"
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}Revoke API key
Revoke (remove) an existing API key.
curl --request POST \
--url https://api.arcus.xyz/v1/revokeApiKey \
--header 'Content-Type: application/json' \
--data '
{
"address": "0x742d35cc6634c0532925a3b844bc9e7595f2bd18",
"publicKey": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
"apiWalletName": "Arcus",
"signature": {
"r": "0x69e7308419769592ec8363d002e3077e803e403b4e1920fc30e485b63d76e2a3",
"s": "0x0980d0742c9e65037af9d54e559704ae2c2285f3112a9d89544191a740f103cf",
"v": "0x1c"
},
"nonce": "a1b2c3d4-5f60-7182-93a4-b5c6d7e8f901"
}
'import requests
url = "https://api.arcus.xyz/v1/revokeApiKey"
payload = {
"address": "0x742d35cc6634c0532925a3b844bc9e7595f2bd18",
"publicKey": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
"apiWalletName": "Arcus",
"signature": {
"r": "0x69e7308419769592ec8363d002e3077e803e403b4e1920fc30e485b63d76e2a3",
"s": "0x0980d0742c9e65037af9d54e559704ae2c2285f3112a9d89544191a740f103cf",
"v": "0x1c"
},
"nonce": "a1b2c3d4-5f60-7182-93a4-b5c6d7e8f901"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
address: '0x742d35cc6634c0532925a3b844bc9e7595f2bd18',
publicKey: 'a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2',
apiWalletName: 'Arcus',
signature: {
r: '0x69e7308419769592ec8363d002e3077e803e403b4e1920fc30e485b63d76e2a3',
s: '0x0980d0742c9e65037af9d54e559704ae2c2285f3112a9d89544191a740f103cf',
v: '0x1c'
},
nonce: 'a1b2c3d4-5f60-7182-93a4-b5c6d7e8f901'
})
};
fetch('https://api.arcus.xyz/v1/revokeApiKey', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.arcus.xyz/v1/revokeApiKey",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'address' => '0x742d35cc6634c0532925a3b844bc9e7595f2bd18',
'publicKey' => 'a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2',
'apiWalletName' => 'Arcus',
'signature' => [
'r' => '0x69e7308419769592ec8363d002e3077e803e403b4e1920fc30e485b63d76e2a3',
's' => '0x0980d0742c9e65037af9d54e559704ae2c2285f3112a9d89544191a740f103cf',
'v' => '0x1c'
],
'nonce' => 'a1b2c3d4-5f60-7182-93a4-b5c6d7e8f901'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.arcus.xyz/v1/revokeApiKey"
payload := strings.NewReader("{\n \"address\": \"0x742d35cc6634c0532925a3b844bc9e7595f2bd18\",\n \"publicKey\": \"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2\",\n \"apiWalletName\": \"Arcus\",\n \"signature\": {\n \"r\": \"0x69e7308419769592ec8363d002e3077e803e403b4e1920fc30e485b63d76e2a3\",\n \"s\": \"0x0980d0742c9e65037af9d54e559704ae2c2285f3112a9d89544191a740f103cf\",\n \"v\": \"0x1c\"\n },\n \"nonce\": \"a1b2c3d4-5f60-7182-93a4-b5c6d7e8f901\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.arcus.xyz/v1/revokeApiKey")
.header("Content-Type", "application/json")
.body("{\n \"address\": \"0x742d35cc6634c0532925a3b844bc9e7595f2bd18\",\n \"publicKey\": \"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2\",\n \"apiWalletName\": \"Arcus\",\n \"signature\": {\n \"r\": \"0x69e7308419769592ec8363d002e3077e803e403b4e1920fc30e485b63d76e2a3\",\n \"s\": \"0x0980d0742c9e65037af9d54e559704ae2c2285f3112a9d89544191a740f103cf\",\n \"v\": \"0x1c\"\n },\n \"nonce\": \"a1b2c3d4-5f60-7182-93a4-b5c6d7e8f901\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.arcus.xyz/v1/revokeApiKey")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"address\": \"0x742d35cc6634c0532925a3b844bc9e7595f2bd18\",\n \"publicKey\": \"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2\",\n \"apiWalletName\": \"Arcus\",\n \"signature\": {\n \"r\": \"0x69e7308419769592ec8363d002e3077e803e403b4e1920fc30e485b63d76e2a3\",\n \"s\": \"0x0980d0742c9e65037af9d54e559704ae2c2285f3112a9d89544191a740f103cf\",\n \"v\": \"0x1c\"\n },\n \"nonce\": \"a1b2c3d4-5f60-7182-93a4-b5c6d7e8f901\"\n}"
response = http.request(request)
puts response.read_body{
"apiKey": "<string>",
"address": "<string>",
"revokedAt": 123,
"allSubaccounts": true,
"accountIndex": 4
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}{
"error": "rate limited",
"reason": "account_empty",
"retryAfterMs": 850,
"clientId": "my-order-42"
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}address (the same wallet that originally signed the matching createApiKey). The gateway recovers the signer and rejects mismatches with HTTP 401. This endpoint does not require an X-API-Key header so the owner can still revoke a key that has been lost or compromised.
Revocation is scoped to (address, publicKey): only the account that owns the key may revoke it. A revoke for a key not registered to the caller’s account returns HTTP 404 and is a no-op — no state is changed.
Signing the request
Identical tocreateApiKey — same Arcus API Key domain, same per-environment chainId values, no verifyingContract — except the primary type is RevokeApiKey and the message does not include validUntil (revoke applies regardless of the key’s remaining lifetime).
Domain
{
"name": "Arcus API Key",
"version": "1",
"chainId": <rootchain chain id, e.g. 421614>
}
createApiKey: sign the accountIndex-bearing type, and for a non-zero index — including the 255 all-subaccounts sentinel — it is mandatory. The value must match the scope the key was created against. Index 0 may still use the original index-less type.
RevokeApiKey(string apiWalletName,string apiWalletPublicKey,uint8 accountIndex)
RevokeApiKey(string apiWalletName,string apiWalletPublicKey)
nonce in the request body, sign the combined type that binds both replay protection and subaccount scope:
RevokeApiKey(string apiWalletName,string apiWalletPublicKey,string nonce,uint8 accountIndex)
nonce and accountIndex in the message must match the request body. For accountIndex 0 the server also accepts the nonce-only type without accountIndex in the signed payload (backward compatibility).
Message
{
"apiWalletName": "<apiWalletName>",
"apiWalletPublicKey": "<publicKey>",
"accountIndex": <accountIndex>
}
eth_signTypedData_v4 call (ethers v6 / viem)
// ethers v6
const domain = {
name: "Arcus API Key",
version: "1",
chainId: 421614 // staging
};
const types = {
RevokeApiKey: [
{ name: "apiWalletName", type: "string" },
{ name: "apiWalletPublicKey", type: "string" }
]
};
const message = {
apiWalletName: "Arcus",
apiWalletPublicKey: pubKeyHex // 64 hex chars, no 0x
};
const sig = await signer.signTypedData(domain, types, message);
// Split into r / s / v for the request body:
const r = sig.slice(0, 66);
const s = "0x" + sig.slice(66, 130);
const v = "0x" + sig.slice(130, 132);
// viem
const sig = await walletClient.signTypedData({ domain, types, primaryType: "RevokeApiKey", message });
Legacy EIP-191 signatures (deprecated)
This endpoint previously accepted an EIP-191personal_sign signature over the canonical JSON message
{"apiWalletName":"<apiWalletName>","apiWalletPublicKey":"<publicKey>"}
Body
Ethereum address that owns the API key to revoke.
^(0x|0X)?[0-9a-fA-F]{40}$"0x742d35cc6634c0532925a3b844bc9e7595f2bd18"
Hex-encoded Ed25519 public key that identifies the API key to revoke.
64^[0-9a-fA-F]{64}$"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
Name of the API wallet (included in the signing message, must match createAPIKey).
1 - 64"Arcus"
secp256k1 EIP-712 typed-data signature (r, s, v) over the RevokeApiKey payload (eth_signTypedData_v4), produced by the wallet that owns address — must be the same wallet that originally signed the matching createApiKey. See the POST /v1/revokeApiKey endpoint description for the exact domain, types, and message. During the migration window the deprecated legacy EIP-191 personal_sign scheme is also accepted. Requests where the recovered signer does not equal address are rejected with HTTP 401.
Show child attributes
Show child attributes
Subaccount scope the key belongs to: 0–9 for a single subaccount, or 255 for an all-subaccounts key. Defaults to 0; must match the value the key was created against. Bound into the signed payload with the same rule as createApiKey (any non-zero value, including 255, requires the accountIndex-bearing RevokeApiKey type).
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255 Optional replay-protection nonce, single-use per (address, accountIndex) when included in the signed EIP-712 payload. See CreateApiKeyRequest.nonce for format and migration-window behavior.
64"a1b2c3d4-5f60-7182-93a4-b5c6d7e8f901"
Response
API key revocation accepted and dispatched to the matching engine.
API key (hex string) that was revoked.
20-byte EVM address as hex: optional 0x or 0X prefix and exactly 40 hexadecimal digits. API responses normalize to lowercase a–f after 0x.
^(0x|0X)?[0-9a-fA-F]{40}$Revocation timestamp (epoch microseconds).
True when the revoked key traded every subaccount of address. Populated on every response; use it as the scope discriminator (optional in schema for deploy-order compatibility).
The single subaccount (0–9) the revoked key traded. Present only when allSubaccounts is false; omitted for an all-subaccounts key.
0 <= x <= 9Was this page helpful?