Skip to content

Auth

API Key Login

Authentication

Provision an API key via the GRVT UI, then exchange it for a session cookie.

# These are the variables you will need to set manually
GRVT_API_KEY="<insert_key_here>"
GRVT_SUB_ACCOUNT_ID="<insert_sub_account_id_here>"

Then, choose the environment you want to authenticate against.

# stg
GRVT_AUTH_ENDPOINT="https://edge.staging.gravitymarkets.io/auth/api_key/login"
# testnet
GRVT_AUTH_ENDPOINT="https://edge.testnet.grvt.io/auth/api_key/login"
# prod
GRVT_AUTH_ENDPOINT="https://edge.grvt.io/auth/api_key/login"

Now, let's create a cookie that will be used to authenticate with our API Services.

echo $GRVT_API_KEY
echo $GRVT_SUB_ACCOUNT_ID
echo $GRVT_AUTH_ENDPOINT

RESPONSE=$(
    curl $GRVT_AUTH_ENDPOINT \
        -H 'Content-Type: application/json' \
        -H 'Cookie: rm=true;' \
        -d "{ \"api_key\": \"$GRVT_API_KEY\" }" \
        -s -i
);

GRVT_COOKIE=$(echo "$RESPONSE" | grep -i 'set-cookie:' | grep -o 'gravity=[^;]*')

echo $GRVT_COOKIE
echo "$RESPONSE" | tail -1

Response

On success (200 OK), a session cookie (gravity=...) is set and the response body contains:

{
  "status": "success",
  "location": "",
  "funding_account_address": "0xYourFundingAccountAddress",
  "sub_account_id": "123456789"
}
Field Type Required Description
status string Yes Login result status (e.g. "success")
location string Yes
funding_account_address string Yes The funding account address associated with the account
sub_account_id string No The sub-account ID, present only when the API key was generated from a Trading Account

Wallet Login

Alternatively, you can authenticate directly with your EVM signing wallet using an EIP-712 typed-data signature — no API key required.

Endpoint: POST /auth/wallet/login

Signing Payload

Construct and sign the following EIP-712 typed data:

Domain

Field Value
name GRVT Exchange
version 0
chainId GRVT chain ID — see Chain IDs

Struct

WalletLogin(address signer, uint32 nonce, int64 expiration)
Field Type Description
signer address Your EVM wallet address (the main signing wallet registered on GRVT)
nonce uint32 A random number chosen by the client. Each (address, nonce) pair can only be used once within its validity window.
expiration int64 Unix timestamp in nanoseconds. Must be in the future. The server enforces a maximum of 5 minutes from the time of the request — signatures with a longer window are rejected with 400. Use the Server Time endpoint to get the current server time.

Sign the struct with eth_signTypedData_v4 to produce V, R, S.

Request

The signature field uses the common Signature DTO shared across all signed endpoints.

{
  "address": "0xYourWalletAddress",
  "signature": {
    "signer": "0xYourWalletAddress",
    "v": 27,
    "r": "0x<32-byte hex>",
    "s": "0x<32-byte hex>",
    "nonce": 305419896,
    "expiration": "1735689600000000000",
    "chain_id": "325"
  }
}
Field Type Required Description
address string Yes Wallet address with 0x prefix
signature.signer string Yes The address (public key) of the wallet signing the payload
signature.v integer Yes Recovery ID — must be 27 or 28
signature.r string Yes Hex-encoded R component
signature.s string Yes Hex-encoded S component
signature.nonce uint32 Yes Random nonce used when constructing the signed payload
signature.expiration string Yes Expiration in nanoseconds as a string (must match the signed value)
signature.chain_id string Yes GRVT L2 chain ID as a string — see Chain IDs. Zero falls back to GRVT default.

Response

On success (200 OK), a session cookie (gravity=...) is set and the response body contains:

{
  "status": "success",
  "location": "",
  "funding_account_address": "0xYourFundingAccountAddress",
  "sub_account_id": "123456789"
}
Field Type Required Description
status string Yes Login result status (e.g. "success")
location string Yes
funding_account_address string Yes The funding account address associated with the account
sub_account_id string No The sub-account ID, present only when the API key was generated from a Trading Account

Use the session cookie to authenticate subsequent API calls in the same way as API key login.

Example (ethers.js)

import { ethers } from "ethers";

// Set GRVT_AUTH_ENDPOINT to your environment (see API Key Login above)
const GRVT_AUTH_ENDPOINT = "<insert_auth_endpoint_here>";

const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();

const GRVT_CHAIN_ID = 325;

const domain = {
  name: "GRVT Exchange",
  version: "0",
  chainId: GRVT_CHAIN_ID,
};

const types = {
  WalletLogin: [
    { name: "signer",     type: "address" },
    { name: "nonce",      type: "uint32"  },
    { name: "expiration", type: "int64"   },
  ],
};

// Fetch server time and compute expiration (max 5 minutes, server-enforced)
const { server_time } = await fetch("https://market-data.grvt.io/time").then(r => r.json());
const expiration = BigInt(server_time) * BigInt(1_000_000)  // ms → ns
                 + BigInt(5 * 60) * BigInt(1_000_000_000);  // + 5 minutes in ns
const nonce = Math.floor(Math.random() * 0xFFFFFFFF);
const address = await signer.getAddress();

const rawSig = await signer.signTypedData(domain, types, { signer: address, nonce, expiration });
const sig = ethers.Signature.from(rawSig);

const response = await fetch(`${GRVT_AUTH_ENDPOINT}/wallet/login`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    address,
    signature: {
      signer: address,
      v: sig.v,
      r: sig.r,
      s: sig.s,
      nonce,
      expiration: expiration.toString(),
      chain_id: GRVT_CHAIN_ID.toString(),
    },
  }),
  credentials: "include",
});

const GRVT_COOKIE = response.headers.get("set-cookie")?.match(/gravity=[^;]*/)?.[0];
const { status, funding_account_address, sub_account_id } = await response.json();