Generating x-pch-digest Header for Online Ordering APIs

The x-pch-digest is a header that must be included with most Punchh online ordering API calls. The x-pch-digest header adds another layer of security to the API calls between your application and the Punchh server by verifying that the encrypted signature sent to Punchh matches the expected hash based on your application's secret key.

Online Ordering vs. Mobile APIs

The x-pch-digest header is required for the online ordering APIs and the mobile APIs. The header is generated in the same way, but there is a difference. The online ordering APIs use SHA1 encryption, while the mobile APIs use SHA256 encryption, so remember to encrypt your hash accordingly depending on the API you are targeting. This topic is focused on the online ordering APIs. Refer to the mobile API topic if you are generating the header for those APIs.

Syntax

The x-pch-digest header is created by concatenating the request URL and the request body and creating a SHA1 hash with your client secret.

hmac("sha1", concat(uri, body), secret)

Field Description
URI The URI should match the request URI, minus the base URI.
Body The body should match the request body exactly as it is sent, with white space removed.

For example:

  • You are making a call to https://SERVER_NAME_GOES_HERE.punchh.com/api/auth/checkins/balance. The URI in this case would be: /api/auth/checkins/balance
  • You are sending the following request body: {"client":"CLIENT_GOES_HERE"}
  • Therefore, you should concatenate the following payload: /api/auth/checkins/balance{"client":"CLIENT_GOES_HERE"}
  • Creating a SHA1 hash with this payload and your client secret will generate the same hash that the Punchh server would generate when it receives your API call, thereby passing the security check.

Common Issues

If you receive a 412 error from the API, the issue is that the x-pch-digest header you are sending does not match what the Punchh server is generating. Here are some common things to check:

  • Make sure that the request URI you are concatenating starts with a / and does not contain the base URI, such as: https://SERVER_NAME_GOES_HERE.punchh.com
  • Make sure that the request body you are concatenating matches exactly what you are sending in the API call and does not include extra white space for indentation.
  • If the API call is a GET request that includes a request body, some API libraries will ignore any body parameters, so the request body you are sending will actually be null. For example, JavaScript libraries such as XMLHttpRequest, fetch, and axios will disregard GET bodies. If this is the case for you, send the body parameters as query string parameters instead.
    For the previous example, your request URI would change to /api/auth/checkins/balance?client=CLIENT_GOES_HERE and the body would be null, so you would end up creating a hash with /api/auth/checkins/balance?client=CLIENT_GOES_HERE and your client secret.

Code Examples

JavaScript

// You will need to install crypto-js before this code will work. To do so, run this at a command prompt inside your project directory:
// npm install crypto-js
function generateSignature() {
    var CryptoJS = require("crypto-js");
    const key = "SECRET_GOES_HERE";
    // Make sure this path is the path from the endpoint you are using:
    const urlPath = '/api/auth/customers.json';
    // Request body example for a 'Sign up with email and password' (/api/auth/customers.json) API call
    const requestBody = JSON.stringify({ client: "CLIENT_ID_GOES_HERE", user: { email: "test@example.com", password: "PASSWORD_GOES_HERE", password_confirmation: "PASSWORD_GOES_HERE", first_name: "FIRST_NAME_GOES_HERE", last_name: "LAST_NAME_GOES_HERE", terms_and_conditions: true, zip_code: "98701", phone: "1111111111", birthday: "1985-10-26" } });
    const payload = urlPath + requestBody;
    const signature = CryptoJS.HmacSHA1(payload, key).toString();

    console.log("Signature: " + signature);
};

generateSignature();

Ruby

require 'json'
require 'net/http'
require 'openssl'
require 'uri'

def create_request(uri)
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true
    # Make sure the HTTP request method here matches the type of request you are making (GET, POST, PATCH, etc.):
    req = Net::HTTP::Post.new(uri)
    return req
end

def generate_signature
    key = "SECRET_GOES_HERE"
    # Make sure this URL is the URL for the endpoint you are using:
    url = "https://SERVER_NAME_GOES_HERE.punchh.com/api/auth/customers.json"
    uri = URI("#{url}")

    request = create_request(uri)

    # Request body example for a 'Sign up with email and password' (/api/auth/customers.json) API call
    request.body = {client:"CLIENT_ID_GOES_HERE",user: {email:"test@example.com",password:"PASSWORD_GOES_HERE",password_confirmation:"PASSWORD_GOES_HERE",first_name:"FIRST_NAME_GOES_HERE",last_name:"LAST_NAME_GOES_HERE",terms_and_conditions:true,zip_code:"98701",phone:"1111111111",birthday:"1985-10-26"}}.to_json

    payload = uri.path + request.body
    signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), key, payload)
    puts "Signature: " + signature
end

generate_signature

Java

import java.security.SignatureException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Formatter;
public class Signature {
private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
private static String toHexString(byte[] bytes) {
Formatter formatter = new Formatter();
for (byte b : bytes) {
formatter.format("%02x", b);
}
return formatter.toString();
}
public static void main(String args[])throws Exception{
String secret = SECRET_KEY_GOES_HERE;
// Make sure this path is the path from the endpoint you are using:
String uriPath = "/api/auth/customers.json";
String requestBody = "{\"client\":\"CLIENT_ID_GOES_HERE\",\"user\": {\"email\": \"test@example.com\",\"password\": \"PASSWORD_GOES_HERE\"}}";
String data = uriPath + requestBody;
// Get an hmac_sha1 key from the raw key bytes
SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(), HMAC_SHA1_ALGORITHM);
// Get an hmac_sha1 Mac instance and initialize with the signing key
Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
mac.init(signingKey);
// Compute the hmac on input data bytes
byte[] rawHmac = mac.doFinal(data.getBytes());
System.out.println(toHexString(rawHmac));
}
}

PHP

PHP

<?php

$host = 'https://SERVER_NAME_GOES_HERE.punchh.com/api/auth/customers/sign_in.json'; // Login API end point
$client = 'CLIENT_GOES_HERE'; // Client ID of business to be used to make API calls
$secret = 'SECRET_GOES_HERE'; // Secret of business to be used to make API calls
$body = '{"client": "CLIENT_GOES_HERE","user":{"email": "test@example.com","password": "PASSWORD_GOES_HERE"}}'; // Body in JSON format

$parsed_host = parse_url($host);
$url_path = $parsed_host['path']; // Path of URL: /api/auth/customers/sign_in.json

$x_pch_digest = hash_hmac('sha1', $url_path.$body, $secret); // Signature specific to every request using SHA1 which is to be passed in `x-pch-digest` key in header

Python

import hmac
import hashlib

def generate_signature():
    secret = 'SECRET_GOES_HERE'
    # Make sure this path is the path from the endpoint you are using:
    path = '/api/auth/customers.json'
    # Request body example for a 'Sign up with email and password' (/api/auth/customers.json) API call
    request_body = json.dumps({
  "client": "CLIENT_GOES_HERE",
  "user": {
    "email": "test@example.com",
    "password": "PASSWORD_GOES_HERE",
    "password_confirmation": "PASSWORD_GOES_HERE",
    "first_name": "FIRST_NAME_GOES_HERE",
    "last_name": "LAST_NAME_GOES_HERE",
    "terms_and_conditions": True,
    "zip_code": "98701",
    "phone": "1111111111",
    "birthday": "1985-10-26"
  }
})
    
    payload = ''.join((path,(request_body)))
    signature = hmac.new(bytes(secret, 'UTF-8'), bytes(payload, 'UTF-8'), hashlib.sha1).hexdigest()
    print(signature)

generate_signature()
Copyright © 2025 PAR Technology Corporation. All rights reserved.
PAR Technology Corporation 8383 Seneca Turnpike, Suite 3 New Hartford, New York 13413 (315) 738-0600 legal@partech.com. PAR Tech is a leading global provider of software, systems, and service solutions to the restaurant and retail industries.
You may learn about its product offerings here.
Before using this application, please read the Limited License Agreement and the PAR Tech Terms of Use.