# API Signature Authentication Guide As an extra layer of security, the Fig Markets API supports HMAC-SHA256 signatures to verify that requests are coming from authorized clients and haven't been tampered with during transmission. This guide explains how to authenticate your API requests using HMAC-SHA256 signatures. Currently, all `GET`, `POST`, `PUT` and `DELETE` requests require signature authentication. ## How It Works 1. **Request Preparation**: Your client creates a request with method, URI, and body 2. **String Construction**: A specific string format is created from your request 3. **Signature Generation**: HMAC-SHA256 is used to sign the string with your client secret 4. **Header Addition**: The signature and timestamp are added as HTTP headers 5. **Request Transmission**: The signed request is sent to the API ## Required Headers Every authenticated request must include these headers: | Header | Description | Example | | --- | --- | --- | | `X-FIG-Signature` | HMAC-SHA256 signature of the request | `a1b2c3d4e5f6...` | | `X-FIG-Timestamp` | Unix timestamp when request was created | `1703123456` | | `Authorization` | JWT bearer token | `Bearer eyJhbGciOiJSUzI1NiIs...` | ## String to Sign Format The signature is calculated from a string with this exact format: ``` \n\n\n ``` The timestamp used to generate the signature must be the same as the `X-FIG-Timestamp` header. ### Examples **DELETE Request (Delete RFQ):** ``` 1703123456 DELETE /rfq/12345 ``` **POST Request (Create RFQ):** ``` 1703123456 POST /rfq {"baseCurrency":"BTC","quoteCurrency":"USD","amount":1.5,"anonymous":false,"settlementCredentials":"DBT-main","legs":[{"direction":"buy","instrumentId":12345,"ratio":1}]} ``` **GET Request (Get RFQ by ID):** ``` 1703123456 GET /rfq/12345 ``` **Important Notes:** - Use uppercase HTTP methods (GET, POST, PUT, DELETE) - Include the full URI path with query parameters - For `GET` requests, the body is empty (just two newlines) - For `DELETE` requests with path parameters, the body is empty (just two newlines) - For `DELETE` requests with request body, include the exact JSON body - For `POST`/`PUT` requests, include the exact JSON body - Maintain exact newline characters (`\n`) ## Implementation Examples ### JavaScript/Node.js ```javascript const crypto = require('crypto'); class FigAPIClient { constructor(baseUrl, clientSecret, jwtToken) { this.baseUrl = baseUrl; this.clientSecret = clientSecret; this.jwtToken = jwtToken; } // Generate signature for a request generateSignature(timestamp, method, uri, body = '') { const stringToSign = `${timestamp}\n${method}\n${uri}\n${body}`; const hmac = crypto.createHmac('sha256', this.clientSecret); hmac.update(stringToSign); return hmac.digest('hex'); } // Make an authenticated request async makeRequest(method, endpoint, body = null) { const uri = endpoint; const bodyString = body ? JSON.stringify(body) : ''; const signature = this.generateSignature(timestamp, method, uri, bodyString); const timestamp = Math.floor(Date.now() / 1000).toString(); const headers = { 'X-FIG-Signature': signature, 'X-FIG-Timestamp': timestamp, 'Authorization': `Bearer ${this.jwtToken}`, 'Content-Type': 'application/json' }; const response = await fetch(`${this.baseUrl}${endpoint}`, { method: method, headers: headers, body: bodyString || undefined }); return response; } // Example methods async createRFQ(rfqData) { return this.makeRequest('POST', '/rfq', rfqData); } async deleteRFQ(rfqId) { return this.makeRequest('DELETE', `/rfq/${rfqId}`); } async deleteQuote(quoteData) { return this.makeRequest('DELETE', '/rfq/quote', quoteData); } } // Usage example const client = new FigAPIClient( 'https://api.figmarkets.com/v1', 'your-client-secret', 'your-jwt-token' ); // Create RFQ request const rfqData = { baseCurrency: 'BTC', quoteCurrency: 'USD', amount: 1.5, anonymous: false, settlementCredentials: 'DBT-main', legs: [ { direction: 'buy', instrumentId: 12345, ratio: 1 } ] }; const rfq = await client.createRFQ(rfqData); // Delete RFQ request const deleteResponse = await client.deleteRFQ(12345); // Delete Quote request const quoteData = { rfqId: 12345, rfqQuoteId: 67890, label: 'MyQuote-123' }; const quoteDeleteResponse = await client.deleteQuote(quoteData); ``` ### Python ```python import hmac import hashlib import time import requests import json class FigAPIClient: def __init__(self, base_url, client_secret, jwt_token): self.base_url = base_url self.client_secret = client_secret.encode('utf-8') self.jwt_token = jwt_token def generate_signature(self, timestamp, method, uri, body=''): """Generate HMAC-SHA256 signature for the request""" string_to_sign = f"{timestamp}\n{method}\n{uri}\n{body}" signature = hmac.new( self.client_secret, string_to_sign.encode('utf-8'), hashlib.sha256 ).hexdigest() return signature def make_request(self, method, endpoint, body=None): """Make an authenticated request to the API""" uri = endpoint body_string = json.dumps(body) if body else '' signature = self.generate_signature(timestamp, method, uri, body_string) timestamp = str(int(time.time())) headers = { 'X-FIG-Signature': signature, 'X-FIG-Timestamp': timestamp, 'Authorization': f'Bearer {self.jwt_token}', 'Content-Type': 'application/json' } url = f"{self.base_url}{endpoint}" if method.upper() == 'GET': response = requests.get(url, headers=headers) elif method.upper() == 'DELETE': if body: response = requests.delete(url, headers=headers, json=body) else: response = requests.delete(url, headers=headers) else: response = requests.post(url, headers=headers, json=body) return response def create_rfq(self, rfq_data): """Create a new RFQ""" return self.make_request('POST', '/rfq', rfq_data) def delete_rfq(self, rfq_id): """Delete an RFQ by ID""" return self.make_request('DELETE', f'/rfq/{rfq_id}') def delete_quote(self, quote_data): """Delete a quote""" return self.make_request('DELETE', '/rfq/quote', quote_data) # Usage example client = FigAPIClient( 'https://api.figmarkets.com/v1', 'your-client-secret', 'your-jwt-token' ) # Create RFQ request rfq_data = { 'baseCurrency': 'BTC', 'quoteCurrency': 'USD', 'amount': 1.5, 'anonymous': False, 'settlementCredentials': 'DBT-main', 'legs': [ { 'direction': 'buy', 'instrumentId': 12345, 'ratio': 1 } ] } response = client.create_rfq(rfq_data) print(response.json()) # Delete RFQ request delete_response = client.delete_rfq(12345) print(delete_response.json()) # Delete Quote request quote_data = { 'rfqId': 12345, 'rfqQuoteId': 67890, 'label': 'MyQuote-123' } quote_delete_response = client.delete_quote(quote_data) print(quote_delete_response.json()) ``` ### Go ```go package main import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "net/http" "strings" "time" ) type FigAPIClient struct { BaseURL string ClientSecret string JWTToken string HTTPClient *http.Client } func NewFigAPIClient(baseURL, clientSecret, jwtToken string) *FigAPIClient { return &FigAPIClient{ BaseURL: baseURL, ClientSecret: clientSecret, JWTToken: jwtToken, HTTPClient: &http.Client{}, } } func (c *FigAPIClient) generateSignature(method, uri, body string) string { stringToSign := fmt.Sprintf("%s\n%s\n%s", method, uri, body) mac := hmac.New(sha256.New, []byte(c.ClientSecret)) mac.Write([]byte(stringToSign)) return hex.EncodeToString(mac.Sum(nil)) } func (c *FigAPIClient) makeRequest(method, endpoint string, body interface{}) (*http.Response, error) { uri := endpoint bodyString := "" if body != nil { bodyBytes, err := json.Marshal(body) if err != nil { return nil, err } bodyString = string(bodyBytes) } signature := c.generateSignature(method, uri, bodyString) timestamp := fmt.Sprintf("%d", time.Now().Unix()) url := c.BaseURL + endpoint var req *http.Request var err error if method == "GET" { req, err = http.NewRequest(method, url, nil) } else if method == "DELETE" && body != nil { req, err = http.NewRequest(method, url, strings.NewReader(bodyString)) } else if method == "DELETE" { req, err = http.NewRequest(method, url, nil) } else { req, err = http.NewRequest(method, url, strings.NewReader(bodyString)) } if err != nil { return nil, err } req.Header.Set("X-FIG-Signature", signature) req.Header.Set("X-FIG-Timestamp", timestamp) req.Header.Set("Authorization", "Bearer "+c.JWTToken) req.Header.Set("Content-Type", "application/json") return c.HTTPClient.Do(req) } func (c *FigAPIClient) CreateRFQ(rfqData map[string]interface{}) (*http.Response, error) { return c.makeRequest("POST", "/rfq", rfqData) } func (c *FigAPIClient) DeleteRFQ(rfqId int) (*http.Response, error) { return c.makeRequest("DELETE", fmt.Sprintf("/rfq/%d", rfqId), nil) } func (c *FigAPIClient) DeleteQuote(quoteData map[string]interface{}) (*http.Response, error) { return c.makeRequest("DELETE", "/rfq/quote", quoteData) } // Usage example func main() { client := NewFigAPIClient( "https://api.figmarkets.com/v1", "your-client-secret", "your-jwt-token", ) // Create RFQ request rfqData := map[string]interface{}{ "baseCurrency": "BTC", "quoteCurrency": "USD", "amount": 1.5, "anonymous": false, "settlementCredentials": "DBT-main", "legs": []map[string]interface{}{ { "direction": "buy", "instrumentId": 12345, "ratio": 1, }, }, } resp, err := client.CreateRFQ(rfqData) if err != nil { fmt.Printf("Error: %v\n", err) return } defer resp.Body.Close() fmt.Printf("Create RFQ Status: %s\n", resp.Status) // Delete RFQ request resp, err = client.DeleteRFQ(12345) if err != nil { fmt.Printf("Error: %v\n", err) return } defer resp.Body.Close() fmt.Printf("Delete RFQ Status: %s\n", resp.Status) // Delete Quote request quoteData := map[string]interface{}{ "rfqId": 12345, "rfqQuoteId": 67890, "label": "MyQuote-123", } resp, err = client.DeleteQuote(quoteData) if err != nil { fmt.Printf("Error: %v\n", err) return } defer resp.Body.Close() fmt.Printf("Delete Quote Status: %s\n", resp.Status) } ```