API DocsCreate Ticket

Webhook Configuration

Receive real-time updates about your shipments with webhooks.

What Are Webhooks?

Webhooks are automated messages sent from atoship to your server when events happen:

  • Label created
  • Package shipped
  • Delivery status changed
  • Exception occurred

Benefits:

  • Real-time updates (not polling)
  • Trigger automated workflows
  • Update your systems instantly
  • Improve customer experience

Available Events

Shipment Events

EventTriggerUse Case
shipment.createdLabel purchasedUpdate inventory
shipment.cancelledLabel voidedRestore inventory
shipment.deliveredPackage deliveredSend review request
shipment.returnedReturn receivedProcess refund
shipment.exceptionDelivery issueAlert customer service

Tracking Events

EventTriggerUse Case
tracking.in_transitFirst carrier scanNotify customer
tracking.out_for_deliveryOn delivery truckSame-day alert
tracking.deliveredConfirmed deliveryClose order
tracking.failed_attemptDelivery attempt failedAlert customer
tracking.exceptionIssue occurredInvestigate

Order Events

EventTriggerUse Case
order.importedOrder syncedStart fulfillment
order.updatedOrder modifiedUpdate records
order.cancelledOrder cancelledStop fulfillment

Account Events

EventTriggerUse Case
balance.lowWallet below thresholdAdd funds alert
billing.adjustmentCarrier adjustmentReview charge

Setup Guide

Step 1: Create Endpoint

Your endpoint must:

  • Accept POST requests
  • Use HTTPS (required for production)
  • Return 200 status within 5 seconds
  • Handle retries idempotently

Example endpoint (Node.js/Express):

const express = require('express');
const app = express();

app.post('/webhooks/atoship', express.json(), (req, res) => {
  // Acknowledge immediately
  res.status(200).send('OK');

  // Process asynchronously
  processWebhook(req.body).catch(console.error);
});

async function processWebhook(event) {
  switch (event.type) {
    case 'shipment.delivered':
      await markOrderDelivered(event.data.order_id);
      await sendDeliveryEmail(event.data);
      break;
    case 'tracking.exception':
      await alertCustomerService(event.data);
      break;
    // Handle other events...
  }
}

Step 2: Register Webhook

  1. Go to Settings > Webhooks
  2. Click Add Webhook Endpoint
  3. Enter your URL: https://yoursite.com/webhooks/atoship
  4. Select events to receive
  5. Click Save

Step 3: Copy Webhook Secret

After saving:

  1. Click on your webhook
  2. Copy the Signing Secret
  3. Store securely (environment variable)

Step 4: Verify Signatures

IMPORTANT: Always verify webhook signatures to prevent spoofing.

const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const expectedSig = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');

  // Use timing-safe comparison
  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expectedSig, 'hex')
  );
}

app.post('/webhooks/atoship', express.json(), (req, res) => {
  const signature = req.headers['x-atoship-signature'];
  const secret = process.env.ATOSHIP_WEBHOOK_SECRET;

  if (!verifySignature(req.body, signature, secret)) {
    console.error('Invalid webhook signature');
    return res.status(401).send('Invalid signature');
  }

  // Signature valid, process event
  res.status(200).send('OK');
  processWebhook(req.body);
});

Event Payload Structure

Standard Format

{
  "id": "evt_1234567890abcdef",
  "type": "shipment.delivered",
  "created_at": "2024-01-15T10:30:00Z",
  "data": {
    "shipment_id": "shp_abc123",
    "tracking_number": "9400111899223456789012",
    "carrier": "USPS",
    "service": "Priority Mail",
    "order_id": "order_xyz789",
    "order_reference": "ORD-12345",
    "delivered_at": "2024-01-15T10:28:00Z",
    "delivery_location": "Front door",
    "signed_by": "RESIDENT"
  },
  "metadata": {
    "organization_id": "org_xxxxx",
    "attempt": 1
  }
}

Retry Logic

Automatic Retries

If your endpoint returns non-2xx or times out:

AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 hours
66 hours

After 6 failures, webhook marked as failed.

Handling Retries

Idempotency is critical:

const processedEvents = new Map(); // Use Redis in production

async function processWebhook(event) {
  // Check if already processed
  if (processedEvents.has(event.id)) {
    console.log('Event ' + event.id + ' already processed, skipping');
    return;
  }

  // Mark as processing
  processedEvents.set(event.id, { status: 'processing', timestamp: Date.now() });

  try {
    // Process event
    await handleEvent(event);
    processedEvents.set(event.id, { status: 'completed', timestamp: Date.now() });
  } catch (error) {
    processedEvents.set(event.id, { status: 'failed', error: error.message });
    throw error; // Let retry happen
  }
}

Testing Webhooks

Send Test Event

  1. Go to Settings > Webhooks
  2. Click on your endpoint
  3. Click Send Test Event
  4. Select event type
  5. View response

Local Development

Use ngrok for local testing:

# Install ngrok
npm install -g ngrok

# Expose local port
ngrok http 3000

# Use provided URL
# https://abc123.ngrok.io/webhooks/atoship

Webhook Logs

View delivery attempts:

  1. Go to webhook settings
  2. Click Delivery Logs
  3. See request/response details
  4. Retry failed deliveries

Best Practices

Response Time

Return 200 quickly:

// Good - Acknowledge immediately, process async
app.post('/webhooks', (req, res) => {
  res.status(200).send('OK');
  queue.add('process-webhook', req.body);
});

// Bad - Blocking response
app.post('/webhooks', async (req, res) => {
  await processWebhook(req.body); // May timeout
  res.status(200).send('OK');
});

Monitoring

Track webhook health:

  • Delivery success rate
  • Average response time
  • Failed event types
  • Retry frequency

Troubleshooting

Not Receiving Webhooks

Check:

  1. Endpoint URL correct
  2. HTTPS enabled
  3. Firewall allows atoship IPs
  4. Events subscribed
  5. Webhook enabled

Signature Verification Failing

Common causes:

  • Using parsed JSON instead of raw body
  • Wrong webhook secret
  • Encoding mismatch

Timeout Errors

If webhook times out:

  1. Respond immediately with 200
  2. Process asynchronously
  3. Use message queue
  4. Check server performance

Need Help?

  • API Docs: Full webhook reference
  • Developer Chat: Technical support
  • Email: [email protected]
  • Support: Create Ticket at /dashboard/support

Was this article helpful?