GuidesAPI EndpointsChangelog
Log In
Guides

Weedmaps sends secure HTTP POST requests, also known as callbacks or webhooks, to your configured endpoint when certain order-related events occur. Each request includes a payload containing a JSON-formatted Order object.

Supported Events

The following events are available for the Weedmaps Orders service:

  • Draft – Triggered when an order is saved as a draft.
  • Create – Triggered when an order is created.

You can configure:

  • A dedicated callback URL for each event, or
  • A single default URL to handle all event types.

Configuring Callback URLs

When configuring callback URLs, you can use dynamically insert relevant data into the URL. This is useful for routing callbacks based on the location or account.

Supported Variable

  • {{merchantId}} – The unique identifier for a retailer location (also known as the Weedmaps Menu ID). This ID is the same one used for Menu API integrations.

Example URL

https://integrator-host.com/callbacks/weedmaps/orders?merchant_id={{merchantId}}

At runtime, {{merchantId}} will be replaced with the actual ID associated with the location triggering the callback.


Verifying Callbacks from Weedmaps

Weedmaps sends callback requests with an HMAC SHA256 signature included in the Signature HTTP header. This allows integrators to verify the authenticity of the callback.

Client Secret

Each integration is assigned a unique client secret, which is a version 4 UUID. This secret is used to compute the signature for validating callbacks.

Authenticating the Callback

To verify that a callback was sent by Weedmaps and has not been tampered with:

  1. Compute the HMAC SHA256 digest:

    • Use the JSON body of the callback request as the message.
    • Use the provided client secret as the HMAC key.
  2. Base64-encode the resulting digest.

  3. Compare the Base64-encoded string with the value of the Signature header included in the callback request.

If the values match, the callback is verified as authentic.

Example Usage

Ruby

const crypto = require('crypto');

/**
 * Generates HMAC-SHA256 signature and encodes in Base64.
 * 
 * @param {string} jsonBody - The raw JSON string.
 * @param {string} clientSecret - The shared secret key.
 * @returns {string} - Base64-encoded HMAC signature.
 */
function generateSignature(jsonBody, clientSecret) {
    return crypto.createHmac('sha256', clientSecret)
        .update(jsonBody)
        .digest('base64');
}

/**
 * Verifies HMAC-SHA256 signature using Base64 encoding and secure comparison.
 * 
 * @param {string} jsonBody - The raw JSON string.
 * @param {string} clientSecret - The shared secret key.
 * @param {string} receivedSignature - The Base64-encoded signature to verify.
 * @returns {boolean} - True if the signature is valid, false otherwise.
 */
function verifySignature(jsonBody, clientSecret, receivedSignature) {
    const expectedSignature = generateSignature(jsonBody, clientSecret);

    const expectedBuffer = Buffer.from(expectedSignature, 'base64');
    const receivedBuffer = Buffer.from(receivedSignature, 'base64');

    if (expectedBuffer.length !== receivedBuffer.length) {
        return false;
    }

    return crypto.timingSafeEqual(expectedBuffer, receivedBuffer);
}

// Example usage
const jsonBody = JSON.stringify({ order_id: 'abc123', status: 'accepted' });
const clientSecret = 'super-secret-key-1234';

// Simulate a signature that would be received from a webhook sender
const signatureToSend = generateSignature(jsonBody, clientSecret);

// Simulate receiving and verifying the signature
const isValid = verifySignature(jsonBody, clientSecret, signatureToSend);

console.log('Generated Signature:', signatureToSend);
console.log('Callback is authentic:', isValid);
require 'openssl'
require 'base64'

def verify_signature(json_body, client_secret, received_signature)
  digest = OpenSSL::Digest.new('sha256')
  
  hmac = OpenSSL::HMAC.digest(digest, client_secret, json_body)
  computed_signature = Base64.strict_encode64(hmac)

  # Use secure comparison to prevent timing attacks
  Rack::Utils.secure_compare(computed_signature, received_signature)
end

# Example usage
json_body = '{"order_id":"abc123","status":"accepted"}'
client_secret = 'your-uuid-client-secret-here'
signature_from_header = 'Base64EncodedSignatureFromHeader=='

puts "Callback is authentic: #{verify_signature(json_body, client_secret, signature_from_header)}"

Events

Draft Event

  • Timing: Synchronous callback when a customer reaches checkout — before the order is finalized.

  • Purpose: Confirms item availability and provides accurate estimates for taxes, fees, and totals.

  • Status: "status": "DRAFT"

  • Payload Includes:

    • lineItems with product details and pricing
    • Estimated taxes, fees, and grandTotal
    • Minimal customer information
  • Merchant Action: Respond with a JSON representation of confirmed line items, pricing, taxes, and fees.

  • Outcome: The Weedmaps storefront updates the cart totals based on your response.

  • Timeout/Error: If your system doesn’t respond in time or returns an error, Weedmaps retains the original estimate.

Draft Example

{
  "status": "DRAFT",
  "orderId": "9779604",
  "lineItems": [...],
  "taxes": [...],
  "fees": [...],
  "grandTotal": "20.48"
}

Create Event

  • Timing: Asynchronous callback when a customer confirms the order.

  • Purpose: Delivers full order details to your POS and triggers fulfillment processes.

  • Status: "status": "PENDING"

  • Payload Includes:

    • Complete lineItems
    • Itemized fees and taxes
    • Full customer profile
    • Shipping address, delivery options, and documents (e.g., ID or medical recommendation)
  • Merchant Action: Respond with HTTP 200 or 201 to acknowledge receipt.

  • Retry Policy: If your endpoint times out or returns an error, Weedmaps retries twice.

Create Example

{
  "status": "PENDING",
  "orderId": "9763822",
  "customer": { ... },
  "lineItems": [...],
  "documents": [...],
  "shippingAddress": { ... },
  "requestedSchedulingInfo": { ... }
}

Next Steps

  1. Draft → Confirmation: Use the Draft event to validate pricing and availability before finalizing.

  2. Create → Fulfillment: Use the Create event to kick off fulfillment workflows—update order status via API to reflect progress (e.g., CONFIRMED, READY, COMPLETED).

  3. Error Handling:

    • For Draft, ensure your system is fast and reliable—timeouts fall back to Weedmaps estimates.
    • For Create, implement retries and logging to diagnose any failed callbacks.
  4. Security: Don’t forget to verify signatures on all callbacks using your client secret. (See earlier section for signature validation details.)