API Authentication and Credentials

Learn how mCards APIs authenticate requests using HMAC-SHA256 signing, including header format, signature construction, and code examples.

API Authentication and Credentials

All mCards APIs authenticate requests using HMAC-SHA256 request signing. Every API call must include a valid Authorization header containing your API key and a signature computed from the request body.


Credentials

When you are onboarded as a development partner, mCards provides two credentials:

CredentialPurpose
API Key (hmac_key)Identifies your partner account. Included in the Authorization header.
API Secret (hmac_secret)Used to compute the HMAC-SHA256 signature. Never transmitted in requests.

You receive separate credentials for sandbox (UAT) and production environments. Keep your API secret confidential — it should only exist on your server, never in client-side code or version control.


Authorization Header Format

Every request must include an Authorization header in the following format:

Authorization: HMAC_256 {api_key};{signature}

Where:

  • {api_key} is your API key (the hmac_key provided during onboarding)
  • {signature} is the HMAC-SHA256 hex digest of the request body, computed using your API secret

The key and signature are separated by a semicolon (;) with no spaces.


Computing the Signature

The signature is computed by taking the HMAC-SHA256 hex digest of the JSON request body using your API secret as the key.

Algorithm

signature = HMAC-SHA256(secret, request_body)

Where:

  • secret = Your API secret (hmac_secret)
  • request_body = The raw JSON string of the request body
  • The output is a lowercase hexadecimal string

For requests with a body (POST, PUT, PATCH)

Sign the full JSON request body exactly as it will be sent.

For requests without a body (GET, DELETE)

Sign the JSON-serialized string "null" (the four-character string: n-u-l-l). This matches the behavior of JSON.stringify(null) in JavaScript or json.dumps(None) in Python.


Code Examples

Python

import hmac
import hashlib
import json
import requests

API_KEY = "your_api_key"
API_SECRET = "your_api_secret"
BASE_URL = "https://uat-us.mcards.com"  # or https://app-us.mcards.com for production

def make_request(method, path, body=None):
    """Make an authenticated request to the mCards API."""
    payload_string = json.dumps(body) if body else json.dumps(None)
    
    signature = hmac.new(
        API_SECRET.encode("utf-8"),
        payload_string.encode("utf-8"),
        hashlib.sha256
    ).hexdigest()
    
    headers = {
        "Authorization": f"HMAC_256 {API_KEY};{signature}",
        "Content-Type": "application/json"
    }
    
    response = requests.request(
        method,
        f"{BASE_URL}{path}",
        headers=headers,
        data=payload_string if body else None
    )
    return response.json()

# Example: List distributors
distributors = make_request("GET", "/api/distributors/v1/distributors")

# Example: Create a webhook subscription
webhook = make_request("POST", "/api/webhook/webhooks", {
    "url": "https://your-server.com/webhooks",
    "event_type": "transaction"
})

Ruby

require "openssl"
require "json"
require "net/http"

API_KEY = "your_api_key"
API_SECRET = "your_api_secret"
BASE_URL = "https://uat-us.mcards.com"  # or https://app-us.mcards.com for production

def make_request(method, path, body = nil)
  payload_string = body ? body.to_json : nil.to_json

  signature = OpenSSL::HMAC.hexdigest(
    "sha256",
    API_SECRET,
    payload_string
  )

  uri = URI("#{BASE_URL}#{path}")
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true

  request = case method
            when :get    then Net::HTTP::Get.new(uri)
            when :post   then Net::HTTP::Post.new(uri)
            when :put    then Net::HTTP::Put.new(uri)
            when :delete then Net::HTTP::Delete.new(uri)
            end

  request["Authorization"] = "HMAC_256 #{API_KEY};#{signature}"
  request["Content-Type"] = "application/json"
  request.body = payload_string if body

  response = http.request(request)
  JSON.parse(response.body)
end

# Example: List distributors
distributors = make_request(:get, "/api/distributors/v1/distributors")

JavaScript (Node.js)

const crypto = require("crypto");
const https = require("https");

const API_KEY = "your_api_key";
const API_SECRET = "your_api_secret";
const BASE_URL = "https://uat-us.mcards.com"; // or https://app-us.mcards.com for production

function makeRequest(method, path, body = null) {
  const payloadString = JSON.stringify(body);

  const signature = crypto
    .createHmac("sha256", API_SECRET)
    .update(payloadString)
    .digest("hex");

  const url = new URL(path, BASE_URL);

  return fetch(url, {
    method: method,
    headers: {
      Authorization: `HMAC_256 ${API_KEY};${signature}`,
      "Content-Type": "application/json",
    },
    body: method !== "GET" ? payloadString : undefined,
  }).then((res) => res.json());
}

// Example: List distributors
makeRequest("GET", "/api/distributors/v1/distributors")
  .then((data) => console.log(data));

cURL

# Set your credentials
API_KEY="your_api_key"
API_SECRET="your_api_secret"

# For GET requests (signing null payload)
PAYLOAD="null"
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$API_SECRET" | awk '{print $NF}')

curl -X GET "https://uat-us.mcards.com/api/distributors/v1/distributors" \
  -H "Authorization: HMAC_256 ${API_KEY};${SIGNATURE}" \
  -H "Content-Type: application/json"

# For POST requests (signing the JSON body)
PAYLOAD='{"url":"https://your-server.com/webhooks","event_type":"transaction"}'
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$API_SECRET" | awk '{print $NF}')

curl -X POST "https://uat-us.mcards.com/api/webhook/webhooks" \
  -H "Authorization: HMAC_256 ${API_KEY};${SIGNATURE}" \
  -H "Content-Type: application/json" \
  -d "$PAYLOAD"

Postman Pre-request Script

If you use Postman for testing, add this as a Pre-request Script to automatically sign requests:

// Set these as Postman environment variables:
// - mcards_api_key
// - mcards_api_secret

const apiKey = pm.environment.get("mcards_api_key");
const apiSecret = pm.environment.get("mcards_api_secret");

const body = pm.request.body ? pm.request.body.raw : "null";

const signature = CryptoJS.HmacSHA256(body, apiSecret).toString(CryptoJS.enc.Hex);

pm.request.headers.add({
  key: "Authorization",
  value: `HMAC_256 ${apiKey};${signature}`
});

Environments

mCards provides two environments:

EnvironmentBase URLPurpose
Sandbox (UAT)https://uat-us.mcards.comDevelopment and testing
Productionhttps://app-us.mcards.comLive transactions

API routes are appended to the base URL. For example, to list distributors in sandbox:

GET https://uat-us.mcards.com/api/distributors/v1/distributors
  • Use sandbox for all development and integration testing
  • Each environment has its own set of API credentials
  • API behavior is identical between environments; only the data and base URL differ

Troubleshooting

Common Authentication Errors

ErrorCauseFix
401 Unauthorized — "Invalid Developer Partner"API key not recognizedVerify you are using the correct hmac_key for the target environment
401 Unauthorized — "Inactive Developer Partner"Partner account is disabledContact your mCards partner manager
403 ForbiddenValid credentials but insufficient permissionsVerify your role grants access to the requested API library (see API Scoping and Permissions)

Signature Debugging Checklist

If you receive authentication errors, verify:

  1. Correct secret — You are using the hmac_secret (not the hmac_key) to compute the signature
  2. Correct payload — The signed string exactly matches the raw request body sent over the wire
  3. Correct environment — Sandbox credentials do not work in production and vice versa
  4. JSON serialization — The body is serialized consistently (key order, whitespace) between signing and sending
  5. Encoding — Both the secret and payload are treated as UTF-8 strings

Token Validation (JWKS)

When your application receives a Feature SSO Token or Payments User Token from mCards (during embedded partner onboarding flows), you must validate the token's signature before trusting its claims.

JWKS Endpoint

mCards publishes its public keys in JSON Web Key Set (JWKS) format:

Token TypeJWKS URL
Feature SSO Token{base_url}/.well-known/jwks.json
Payments User Token{base_url}/api/payments/.well-known/jwks.json

Replace {base_url} with your environment's base URL (provided during onboarding).

Validation Steps

  1. Fetch the JWKS — Retrieve the public key set from the appropriate JWKS endpoint. Cache the response (keys rotate infrequently — a 2-hour cache TTL is reasonable).
  2. Decode the JWT header — Extract the kid (key ID) from the token's header to identify which key to use.
  3. Verify the signature — Use the matching public key to verify the token's RSA signature (RS256 algorithm).
  4. Check standard claims — Verify exp (expiration), iss (issuer), and aud (audience) claims match expected values.
  5. Extract identity claims — Use the validated claims (consumer_id, cardholder_card, etc.) to identify the cardholder.

Example: Token Validation in Python

import jwt
import requests

JWKS_URL = "{base_url}/api/payments/.well-known/jwks.json"

def validate_mcards_token(token):
    """Validate an mCards JWT and return its claims."""
    # Fetch JWKS (cache this in production)
    jwks = requests.get(JWKS_URL).json()
    
    # Decode and verify the token
    header = jwt.get_unverified_header(token)
    
    # Find the matching key by kid
    key = next(k for k in jwks["keys"] if k["kid"] == header["kid"])
    public_key = jwt.algorithms.RSAAlgorithm.from_jwk(key)
    
    claims = jwt.decode(
        token,
        public_key,
        algorithms=["RS256"],
        audience="expected_audience"
    )
    return claims

Note: Feature SSO Tokens expire after 5 minutes and are intended for single-use during onboarding. Do not cache or reuse these tokens. See Feature SSO Token and Payments User Token for token-specific claim details.


Webhook Security vs. API Authentication

API authentication (this page) and webhook security are separate mechanisms:

API AuthenticationWebhook Security
DirectionYour server → mCardsmCards → Your server
MechanismHMAC-SHA256 of request bodySignature verification of webhook payload
CredentialsYour API key + secretWebhook-specific signing secret
PurposeProve your identity to mCardsVerify webhook came from mCards

For webhook security details, see Webhooks API Concepts.