AtlasWork, planned itself.

    The AI-native, all-in-one work platform. Tasks, projects, CRM, contracts, and analytics in one calm workspace.

    System status
    • SSO
    • SCIM
    • Two-factor sign-in
    • Audit log

    Product

    • Overview
    • PDF tools
    • Diagram tools
    • People & HR
    • Integrations
    • Marketplace
    • Pricing

    Resources

    • Guides
    • Glossary
    • Compare
    • Docs
    • API reference
    • Support
    • Changelog
    • Status

    Company

    • About
    • Careers
    • Press
    • Contact

    Legal & trust

    • Trust center
    • Security
    • Privacy
    • Terms
    • DPA
    • GDPR
    • SLA
    • Refunds
    • Google API data
    Atlas, a product by wrxstack.com·© 2026 wrxstack·All rights reserved
    PrivacyTermsSecurityStatus

    You are in control of your cookies

    Atlas uses strictly necessary cookies to keep you signed in. With your consent, we add anonymized product analytics, conversion attribution, and remembered preferences. Change your mind any time at /privacy/cookies.

    Off until you agree · Change it any time

    • Necessaryalways on
    • Analyticsopt-in
    • Marketingopt-in
    • Preferencesopt-in
    Skip to documentation
    Docs
    Back to Atlas

    Start here

    • Overview

    Developer

    • REST API guide
    • Authentication
    • API reference
    • MCP (AI agents)
    • MCP tools reference
    • SDKs
    • Quick actions
    • Changelog

    Webhooks

    • Overview
    • Quickstart
    • Events
    • Payloads and headers
    • Security and signing
    • Delivery and retries
    • Managing via API

    Connect

    • Connectors
    • Integrations

    Product

    • Collaboration and chat
    • Signing in and security
    • Client portal

    Reference

    • Glossary
    • Keyboard shortcuts
    • Module reference

    Webhooks

    Security and signing

    Atlas signs every delivery with an HMAC-SHA256 over a timestamp and the raw body. Verify that signature, refuse replays, and rotate your secret without missing a delivery.

    Verify every signature

    Every delivery carries an Atlas-Signature header in this form:

    Atlas-Signature: t=1790000000,v1=<64 hexadecimal characters>
    • t is the moment Atlas signed this attempt, in seconds since the Unix epoch. The same value is sent on its own in the Atlas-Timestamp header.
    • v1 is the lowercase hexadecimal HMAC-SHA256 of the text <t>.<raw body>, keyed with your signing secret. While a rotation is in progress the header carries one v1 for each secret, so accept the delivery when any one of them matches.
    • The raw body means the exact bytes Atlas sent. Never verify against JSON you parsed and serialized again: a change in whitespace or key order changes the digest, and every verification will fail.

    To verify a delivery, compute that HMAC with your secret and compare it with each v1 using a constant-time comparison. A plain === or == comparison reveals timing information that helps an attacker forge a signature, so use the constant-time function your language provides. Refuse the delivery when nothing matches, and refuse it when t is more than five minutes from your own clock. Every attempt is signed at the moment it is sent, so a genuine retry always carries a fresh timestamp.

    import crypto from "node:crypto";
    import express from "express";
    
    const SIGNING_SECRET = process.env.ATLAS_WEBHOOK_SECRET;
    const TOLERANCE_SECONDS = 300;
    const app = express();
    
    // Read the raw body: the signature covers the exact bytes Atlas sent.
    // Never verify against JSON you parsed and serialized again.
    app.post("/atlas/webhook", express.raw({ type: "*/*" }), (req, res) => {
      const header = req.header("atlas-signature") ?? "";
      const body = req.body.toString("utf8");
    
      let timestamp = null;
      const signatures = [];
      for (const part of header.split(",")) {
        const [key, value] = part.split("=", 2);
        if (key === "t" && /^\d+$/.test(value ?? "")) timestamp = Number(value);
        if (key === "v1") signatures.push(value);
      }
      if (timestamp === null || signatures.length === 0) {
        return res.status(400).send("missing signature");
      }
      if (Math.abs(Date.now() / 1000 - timestamp) > TOLERANCE_SECONDS) {
        return res.status(400).send("timestamp outside tolerance");
      }
    
      const expected = crypto
        .createHmac("sha256", SIGNING_SECRET)
        .update(`${timestamp}.${body}`)
        .digest();
      const valid = signatures.some((signature) => {
        const given = Buffer.from(signature, "hex");
        return given.length === expected.length && crypto.timingSafeEqual(given, expected);
      });
      if (!valid) return res.status(401).send("invalid signature");
    
      // Valid. Skip an Atlas-Event-Id you have already processed, queue the
      // work, and answer at once.
      res.status(200).send("ok");
    });

    To check your own implementation, enter a signing secret, a timestamp, and a sample body below. The tool computes the exact Atlas-Signature header Atlas would send. It runs entirely in your browser, so the secret never leaves this page.

    Signature playgroundRuns in your browser only
    Computed signature

    Enter a secret to compute the signature.

    signed message: 1783412096.{"id":"evt_9f2c","type":"task.completed","apiVersion":"2026-10-01","createdAt":"2026-07-07T12:34:56.000Z","tenantId":"ten_123","actor":{"id":"usr_789","kind":"USER"},"data":{"object":{"id":"t_456","status":"DONE"}}}


    Replay protection

    A valid signature proves that a delivery came from Atlas. On its own, it does not stop someone who captured a genuine delivery from sending it to you again. Your receiver needs two further checks.

    The first is the timestamp tolerance. Refuse a delivery whose t is more than five minutes from your clock, in either direction. The timestamp is part of the signed text, so it cannot be changed to look recent without breaking the signature. A captured request is therefore useless after five minutes.

    The second closes those five minutes. Record the Atlas-Event-Id of every event you process, and skip an event you have already processed. Every attempt and every replay of one event carries the same event id, so this check also makes your receiver safe when Atlas retries a delivery your server processed but did not answer in time. Keep each id for at least 35 hours, the length of the full retry schedule, and longer if you replay deliveries by hand.

    Use both checks

    Refuse any delivery whose timestamp is more than five minutes from now, and skip any Atlas-Event-Id you have already processed. A valid signature alone does not protect you against a replay.

    Key rotation

    Rotate a webhook signing secret with POST /v1/webhooks/{id}/rotate-key, using a token that holds the webhooks:manage scope, or from the webhook's page in Settings. The new secret is returned exactly once, so store it before you do anything else.

    curl -X POST https://api.example.com/v1/webhooks/{id}/rotate-key \
      -H "Authorization: Bearer atlas_pat_REPLACE_ME"
    
    # The new secret is returned exactly once:
    # { "id": "...", "signingKeyVersion": 2, "secret": "whsec_...",
    #   "previousSigningKeyExpiresAt": "..." }

    After a rotation, Atlas signs every attempt with both secrets for 58 hours and 45 minutes, and the header carries two v1 values. That period is longer than the full retry schedule, so a delivery that was first attempted before the rotation still carries a signature your receiver can check with the old secret. Move your receiver to the new secret at any point inside the period. The response field previousSigningKeyExpiresAt states when the old secret stops signing.

    Because a receiver accepts a delivery when any v1 matches, the samples above need no change for a rotation: they keep working with whichever secret you have configured.


    Secrets and network

    Your signing secret is shown once

    The signing secret is returned only when the webhook is created and when it is rotated. It cannot be retrieved afterwards. Atlas stores it encrypted, but you hold the only copy you can read: keep it in a secrets manager and never in source control. If it leaks, rotate it at once.

    Endpoints must be public HTTPS addresses

    Atlas delivers only to public HTTPS addresses. Addresses on the local machine, in a private network range, or otherwise not reachable from the internet are refused when the webhook is saved and again before each attempt. To test on your own machine, use a tunnelling tool that gives you a public HTTPS address.

    On this page

    • Verify every signature
    • Replay protection
    • Key rotation
    • Secrets and network