Gaya Logo

Data Mapping

Webhooks

Gaya API sends real-time webhook notifications for clipboard events to your specified endpoint. Instantly trigger custom actions when data is created, modified, or exported. Integrate seamlessly with your systems, update databases, or power automations. Use this playground to test your webhook integration before deployment.

Webhook event catalog

Currently, the Gaya API sends webhooks for the following event:

Event typeTrigger description
EXPORT_CLIPBOARDTriggered once clipboard data has been exported

Webhook payload example

The following example illustrates the structure of webhook payloads, providing information about the event that occurred:

{
    "hook": {
        "id": "9c3fba86-38e5-4655-bf2e-3ef0015c0c44",
        "event": "EXPORT_CLIPBOARD",
        "target": "https://yoururl.com"
    },
    "organization": {
        "user": "[email protected]",
        "name": "John Doe Organization",
        "origination_clipboard": "Progressive",
        "office": "Austin HQ"
    },
    "entities": [
        {
            "entity": "customer",
            "index": 1,
            "fields": [
                {
                    "name": "email",
                    "value": "[email protected]"
                },
                {
                    "name": "first_name",
                    "value": "John"
                },
                {
                    "name": "last_name",
                    "value": "Doe"
                }
            ]
        },
        {
            "entity": "car",
            "index": 1,
            "fields": [
                {
                    "name": "make",
                    "value": "Toyota"
                },
                {
                    "name": "model",
                    "value": "Corolla"
                }
            ]
        },
        {
            "entity": "car",
            "index": 2,
            "fields": [
                {
                    "name": "make",
                    "value": "Honda"
                },
                {
                    "name": "model",
                    "value": "Civic"
                }
            ]
        }
    ],
    "created_at": "2024-09-26T02:47:16.894Z"
}

How to implement webhooks

At a high level, in order to implement webhooks, you'll need to:

  1. Contact Gaya to register a URL to receive the webhooks.

  2. Validate webhook deliveries to ensure they come from Gaya.

  3. Grab the relevant pieces of payload you need and then perform your business logic.

Registering your new endpoint URL

After registering your URL to receive webhooks, Gaya will provide a secret token to validate webhook deliveries. You'll need to store it in a safe place that your server can access. Never hardcode a token into an application or push a token to any repository.

Please reach out to Gaya whenever you want to discontinue receiving webhook events.

Validating webhook deliveries

Gaya will use your secret token to create a hash signature that's sent to your public POST endpoint with each payload. The hash signature will appear in each delivery as the value of the X-Gaya-Signature-256 header.

The signature is sensitive to any changes, so even a small change in the payload will cause the signature to be completely different. This means that you should not change the payload in any way before verifying.

In your code that handles webhook deliveries, you should calculate a hash using your secret token. Then, compare the hash that Gaya sent with the expected hash that you calculated, and ensure that they match. For examples showing how to validate the hashes in various programming languages, see "Examples."

There are a few important things to keep in mind when validating webhook payloads:

  • Gaya uses an HMAC hex digest to compute the hash.

  • The hash signature always starts with sha256=.

  • The hash signature is generated using your webhook's secret token and the payload contents.

  • Never use a plain == operator. Instead consider using a method like secure_compare or crypto.timingSafeEqual, which performs a "constant time" string comparison to help mitigate certain timing attacks against regular equality operators, or regular loops in JIT-optimized languages.

Examples

You can use the programming language of your choice to implement HMAC verification in your code. Below are a few examples of how an implementation can look in various programming languages.


import express, { Request, Response } from 'express';
import * as crypto from 'crypto';

const app = express();
app.use(express.json()); // Middleware to parse JSON bodies

/**
 * Verifies if the HMAC signature in the request header matches the expected signature.
 * 
 * @param req - The request object from Express.js containing the webhook data.
 * @param secretKey - The secret key used for verifying the HMAC signature.
 * @returns true if the signature is valid, false otherwise.
 * @throws {Error} - Throws an error if the secret key is missing or if any other issue occurs during verification.
 */
function verifySignature(req: Request, secretKey: string): boolean {
  if (!secretKey) {
    throw new Error('Secret key for HMAC verification is missing.');
  }

  const signatureHeader = req.headers['x-gaya-signature-256'];
  if (!signatureHeader || typeof signatureHeader !== 'string') {
    throw new Error('Signature header "x-gaya-signature-256" is missing or not in correct format.');
  }

  if (!req.body || Object.keys(req.body).length === 0) {
    throw new Error('Request body is empty.');
  }

  const payload = JSON.stringify(req.body);
  let generatedSignature: string;

  try {
    const hmac = crypto.createHmac('sha256', secretKey);
    generatedSignature = hmac.update(payload).digest('hex');
  } catch (error) {
    throw new Error('Error occurred during HMAC signature generation.');
  }

  const trustedSignature = `sha256=${generatedSignature}`;
  return crypto.timingSafeEqual(Buffer.from(trustedSignature, 'utf8'), Buffer.from(signatureHeader, 'utf8'));
}

/**
 * Handles incoming webhooks and processes them based on the event type.
 * 
 * @param req - The request object from Express.js.
 * @param res - The response object from Express.js.
 * @throws {Error} - Throws an error if the webhook signature verification fails.
 */
function webhookProcessingEndpoint(req: Request, res: Response): void {
  try {
    const WEBHOOK_SECRET: string = process.env.WEBHOOK_SECRET || '';

    if (!verifySignature(req, WEBHOOK_SECRET)) {
      res.status(401).send('Webhook verification failed');
      return;
    }

    const { event_type: eventType } = req.body;

    switch (eventType) {
      case 'EXPORT_CLIPBOARD':
        // Business logic for EXPORT_CLIPBOARD event
        console.log('Processing EXPORT_CLIPBOARD event');
        // Add your EXPORT_CLIPBOARD logic here
        res.status(200).send('EXPORT_CLIPBOARD event processed successfully');
        break;
      default:
        res.status(422).send('The Webhook event type is unknown');
        return;
    }
  } catch (error) {
    console.error(`Error processing webhook: ${error.message}`);
    res.status(500).send('Internal server error while processing the webhook');
  }
}

// Define the route for the webhook endpoint
app.post('/webhook', webhookProcessingEndpoint);

// Start the server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Troubleshooting

If you are sure that the payload is from Gaya but the signature verification fails:

  • Make sure you are using the correct header. Gaya recommends that you use the X-Gaya-Signature-256 header, which uses the HMAC-SHA256 algorithm.

  • Make sure that you are using the correct algorithm. If you are using the X-Gaya-Signature-256 header, you should use the HMAC-SHA256 algorithm.

  • Make sure you are using the correct webhook secret.

  • Make sure that the payload and headers are not modified before verification. For example, if you use a proxy or load balancer, make sure that the proxy or load balancer does not modify the payload or headers.

  • If your language and server implementation define a character encoding, make sure to process the payload as UTF-8.