Skip to Content
Authentication

Authentication

The API uses secure authentication to protect airdrop operations:

  1. Bearer Token - Required for ALL requests
  2. Request Signature - HMAC-SHA256 signature (required for airdrop requests only)

Required Headers

All Requests (Health, Stats, etc.)

Authorization: Bearer YOUR_BEARER_TOKEN

Airdrop Requests

Authorization: Bearer YOUR_BEARER_TOKEN X-Signature: v1,t=...,s=...

Getting Your Credentials

Contact the Movement team to receive:

  • Bearer Token - For authenticating all API requests
  • Signing Secret - For generating HMAC-SHA256 signatures on airdrop requests

Environment-Specific Credentials: Testnet and mainnet use different bearer tokens and signing secrets. Make sure you use the correct credentials for each environment.

Request Signatures

All airdrop requests must include an HMAC-SHA256 signature.

Signature Format

Include the signature in the X-Signature header:

X-Signature: v1,t=1700000000,s=abc123def456...

Where:

  • v1 = signature version
  • t = Unix timestamp (seconds)
  • s = HMAC-SHA256 signature

Generating Signatures

The signature is computed over this payload:

v1.timestamp.METHOD.PATH.BODY

Example payload:

v1.1700000000.POST./api/airdrop.{"address":"0x...","amount":"100","uniqueHash":"abc..."}

TypeScript Example

import crypto from 'crypto'; function generateSignature( signingSecret: string, method: string, path: string, body: object ): string { const timestamp = Math.floor(Date.now() / 1000); const bodyString = JSON.stringify(body); const payload = `v1.${timestamp}.${method}.${path}.${bodyString}`; const signature = crypto .createHmac('sha256', signingSecret) .update(payload) .digest('hex'); return `v1,t=${timestamp},s=${signature}`; } // Usage const signature = generateSignature( SIGNING_SECRET, 'POST', '/api/airdrop', { address: '0x1234...', amount: '100', uniqueHash: 'abc123...' } );

Complete Client Example

import crypto from 'crypto'; class AirdropClient { constructor( private bearerToken: string, private signingSecret: string, private baseUrl: string = 'https://YOUR_API_DOMAIN' ) {} generateSignature(method: string, path: string, body: object): string { const timestamp = Math.floor(Date.now() / 1000); const bodyString = JSON.stringify(body); const payload = `v1.${timestamp}.${method}.${path}.${bodyString}`; const signature = crypto .createHmac('sha256', this.signingSecret) .update(payload) .digest('hex'); return `v1,t=${timestamp},s=${signature}`; } async processAirdrop(address: string, amount: string, uniqueHash: string) { const path = '/api/airdrop'; const body = { address, amount, uniqueHash }; const signature = this.generateSignature('POST', path, body); const response = await fetch(`${this.baseUrl}${path}`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.bearerToken}`, 'X-Signature': signature, 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); if (!response.ok) { const error = await response.json(); throw new Error(`Airdrop failed: ${error.error?.message || 'Unknown error'}`); } return response.json(); } async checkHealth() { const response = await fetch(`${this.baseUrl}/api/health`, { headers: { 'Authorization': `Bearer ${this.bearerToken}`, } }); return response.json(); } } // Initialize client const client = new AirdropClient( process.env.AIRDROP_BEARER_TOKEN!, process.env.AIRDROP_SIGNING_SECRET! ); // Process an airdrop try { const result = await client.processAirdrop( '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', '100', crypto.randomBytes(32).toString('hex') ); console.log('Airdrop successful:', result.transactionHash); } catch (error) { console.error('Airdrop failed:', error.message); }

Compromised Credentials? If you suspect your credentials have been compromised, contact the Movement team immediately to revoke and reissue new credentials.

Common Authentication Errors

401 Unauthorized - Missing Authorization Header

{ "error": "Unauthorized", "message": "Missing or invalid Authorization." }

Solution: Include Authorization: Bearer YOUR_TOKEN header in all requests.

401 Unauthorized - Invalid Bearer Token

{ "error": "Unauthorized", "message": "Invalid bearer token" }

Solution: Verify your bearer token is correct and hasn’t expired.

401 Unauthorized - Invalid Signature

{ "success": false, "error": { "code": "UNAUTHORIZED", "message": "Invalid request signature" } }

Solution: Check your signature generation:

  • Verify signing secret is correct
  • Ensure payload format matches exactly: v1.timestamp.METHOD.PATH.BODY
  • Body must be JSON stringified with no extra whitespace

401 Unauthorized - Expired Signature

{ "success": false, "error": { "code": "UNAUTHORIZED", "message": "Signature timestamp is too old or in the future" } }

Solution:

  • Ensure your system clock is synchronized (use NTP)
  • Don’t reuse signatures (generate fresh for each request)
  • Signatures are valid for 5 minutes

Next Steps

Last updated on