Skip to main content

Overview

Every request to /api/v1/partner/* is authenticated with your API key and an HMAC-SHA256 signature. This proves the request came from you, was not tampered with, and cannot be replayed.

Required Headers

Every request must include all four of these headers:
HeaderValue
x-api-keyYour full ec_live_… / ec_test_… API key
x-timestampCurrent Unix time in whole seconds, as a string (e.g. "1749820800")
x-nonceA unique value per request — a UUID v4 works well
x-signatureHex HMAC-SHA256 over the canonical signing string (see below)
Headless sessions: On calls made after you start a journey (poll, action, redirect-returned), also send the per-journey journeySession in the X-Journey-Session header (or the journey_session cookie). Never put it in Authorization, which is reserved for the API key.

The Canonical Signing String

Build this exact length-prefixed string, then HMAC-SHA256 it with your signingSecret and hex-encode the result:
v1:<len(METHOD)>:<METHOD>:<len(PATH)>:<PATH>:<len(TIMESTAMP)>:<TIMESTAMP>:<len(NONCE)>:<NONCE>:<BODY>

Field Definitions

FieldDescription
METHODThe HTTP verb in uppercase — POST, GET
PATHThe request path without the query string (e.g. /api/v1/partner/journey/jrn_abc/action)
TIMESTAMPThe exact value you put in x-timestamp
NONCEThe exact value you put in x-nonce
BODYThe exact raw request body. For GET requests (no body), use an empty string — don’t omit the field

Example Signing String

For a POST to /api/v1/partner/journey/initiate:
v1:4:POST:34:/api/v1/partner/journey/initiate:10:1749820800:36:550e8400-e29b-41d4-a716-446655440000:{"channel":"API_HEADLESS",...}

Nonce & Timestamp Rules

Nonce Rules

  • Printable ASCII, 1–200 characters
  • Single-use — a replayed nonce is rejected with 401
  • A UUID v4 is recommended

Timestamp Rules

  • Whole seconds (Unix epoch)
  • Tolerance: ±300 seconds from server time
  • Requests outside the window are rejected with 401

Node.js Signing Helper

Drop-in helper that produces the signed headers. The x-signature it returns is what the curl examples in this guide show as <computed-hex>.
import { createHmac, randomUUID } from 'crypto';

function signRequest(
  method,   // 'POST' | 'GET'
  path,     // exclude the query string
  body,     // JSON string, or '' for GET
  apiKey,
  secret,
) {
  const ts    = String(Math.floor(Date.now() / 1000));
  const nonce = randomUUID();
  const str   =
    `v1:${method.length}:${method}:${path.length}:${path}:${ts.length}:${ts}:${nonce.length}:${nonce}:${body}`;
  const sig   = createHmac('sha256', secret).update(str).digest('hex');
  return {
    'Content-Type': 'application/json',
    'x-api-key':    apiKey,
    'x-timestamp':  ts,
    'x-nonce':      nonce,
    'x-signature':  sig,
  };
}

Usage Example

const headers = signRequest(
  'POST',
  '/api/v1/partner/journey/initiate',
  JSON.stringify(requestBody),
  process.env.EASYCRED_API_KEY,
  process.env.EASYCRED_SIGNING_SECRET,
);

const response = await fetch('https://api.easycred.in/api/v1/partner/journey/initiate', {
  method: 'POST',
  headers,
  body: JSON.stringify(requestBody),
});

Common Authentication Errors

ErrorCauseFix
401 — bad signatureWrong signingSecret or malformed canonical stringVerify the signing string construction
401 — stale timestampx-timestamp is more than 300s from server timeSync your server clock (use NTP)
401 — replayed nonceSame nonce used twiceGenerate a fresh UUID per request
401 — missing journey sessionHeadless call without X-Journey-SessionAdd the journeySession from the initiate response