ERP Event Messaging Outbox Table SQL Generator
If an ERP posts a message directly to WhatsApp or Teams inside a transaction, a slow API call holds locks and a failed one loses the notification. A transactional outbox solves it: write the message to a table with the business change, and let a worker send it. This generator produces the SQL.
Fill in
PostgreSQL: table
-- Generic names: adapt column names and keys to your own schema. CREATE TABLE IF NOT EXISTS messaging_outbox ( id BIGSERIAL PRIMARY KEY, channel VARCHAR(20) NOT NULL, event_type VARCHAR(60) NOT NULL, dedupe_key VARCHAR(120) NOT NULL, recipient VARCHAR(120) NOT NULL, payload JSONB NOT NULL, status VARCHAR(12) NOT NULL DEFAULT 'pending', attempts INT NOT NULL DEFAULT 0, next_attempt TIMESTAMPTZ NOT NULL DEFAULT now(), provider_id VARCHAR(120), last_error TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), sent_at TIMESTAMPTZ, CONSTRAINT uq_messaging_outbox_dedupe UNIQUE (channel, dedupe_key) ); CREATE INDEX IF NOT EXISTS ix_messaging_outbox_due ON messaging_outbox (next_attempt) WHERE status = 'pending';
PostgreSQL: enqueue in the same transaction as the business change
INSERT INTO messaging_outbox (channel, event_type, dedupe_key, recipient, payload)
VALUES ('whatsapp', 'shipment_dispatched', 'SO-104522-dispatch', '919800000000',
'{"order":"SO-104522","eta":"2026-10-21"}')
ON CONFLICT (channel, dedupe_key) DO NOTHING;PostgreSQL: worker claims a batch
WITH due AS ( SELECT id FROM messaging_outbox WHERE status = 'pending' AND next_attempt <= now() AND attempts < 6 ORDER BY next_attempt LIMIT 20 FOR UPDATE SKIP LOCKED ) UPDATE messaging_outbox o SET status = 'sending', attempts = o.attempts + 1 FROM due WHERE o.id = due.id RETURNING o.id, o.channel, o.recipient, o.payload;
PostgreSQL: record the result
-- On success
UPDATE messaging_outbox SET status = 'sent', sent_at = now(), provider_id = 'wamid.XXXX' WHERE id = 1;
-- On transient failure: exponential backoff, back to pending
UPDATE messaging_outbox
SET status = 'pending', last_error = 'HTTP 429',
next_attempt = now() + (power(2, attempts) * interval '30 seconds')
WHERE id = 1;
-- Give up after the maximum attempts
UPDATE messaging_outbox SET status = 'failed' WHERE status = 'pending' AND attempts >= 6;SQL Server: worker claim equivalent
WITH due AS ( SELECT TOP (20) * FROM messaging_outbox WITH (UPDLOCK, READPAST, ROWLOCK) WHERE status = 'pending' AND next_attempt <= SYSUTCDATETIME() AND attempts < 6 ORDER BY next_attempt ) UPDATE due SET status = 'sending', attempts = attempts + 1 OUTPUT inserted.id, inserted.channel, inserted.recipient, inserted.payload;
Table and column names here are generic, not an ERP vendor schema. Insert the outbox row in the same database transaction as the business change, then let a separate worker send. Delivery is at least once, so rely on the dedupe key and provider message IDs to avoid duplicates. SQL Server needs a datetime2 type for next_attempt and nvarchar(max) for payload; adjust types. Reset rows stuck in sending after a timeout.
Outbox design review
Optional review of your ERP notification pipeline for reliability and duplicates.
Optional. The tool and every export work without this. No spam.
How to use the SQL
Run the table script in a database you control, not inside a vendor ERP schema unless your vendor supports custom tables. Have your integration or a trigger insert a row when the business event commits. Run one or more workers that claim a batch with the locking query, call the messaging API, and write the result. Choose the PostgreSQL or SQL Server variant that matches your platform.
- The unique dedupe key stops the same event queuing twice.
- SKIP LOCKED or READPAST lets several workers run without blocking.
- Backoff pushes next_attempt out after each transient failure.
- A failed status gives support a list of messages that need a human.
Why the outbox pattern
It separates the business commit from the network call. If the messaging API is down, the ERP transaction still succeeds and the queue drains later. If the worker crashes after sending but before updating, the message may be sent again, so delivery is at least once and the provider message ID or dedupe key is your protection.
Worked example
A shipment is posted at 16:05. The same transaction inserts an outbox row with the dedupe key SO-104522-dispatch. A worker claims it at 16:05:10, calls the WhatsApp template send, stores the returned message ID and marks it sent. If the call returns 429, the row returns to pending with next_attempt 60 seconds later and attempts incremented.
Common mistakes
Forgetting an index on due rows makes the polling query slow once the table grows. Never purging sent rows lets the table balloon, so archive or delete after a retention period. Leaving rows in sending forever after a worker crash silently drops messages.
Frequently Asked Questions
What is a transactional outbox?
It is a table where an application writes the messages it needs to send, in the same transaction as its business data. A separate process reads the table and delivers the messages, so a failure in delivery never rolls back or loses the business change.
Why use SKIP LOCKED or READPAST?
They let several workers pull rows from the same table without waiting on each other. Each worker skips rows another worker has locked, which gives parallel throughput without double sending the same row.
Does this give exactly once delivery?
Not strictly. It gives at least once delivery with strong duplicate protection from the dedupe key and stored provider IDs. True exactly once across an external API is not generally achievable.
Can I add this to a vendor ERP database?
Check your ERP vendor's support terms first. Many teams put the outbox in a separate integration database and replicate or call into it, to avoid upgrade and support conflicts with the vendor schema.
If you want a reliable outbox and worker layer between your ERP and messaging channels, Netray can design and run it with your DBA team.
Related Tools
Messaging Webhook Retry Backoff Schedule Calculator
Work out how long an exponential backoff retry policy keeps trying before a WhatsApp, Teams or Slack message is parked for review.
Developer ToolsMessaging Throughput and Rate Limit Headroom Calculator
Estimate peak message rate, how much headroom you have under a rate limit, how many workers you need and how long a burst takes to drain.
Developer ToolsWhatsApp Cloud API Send Template Request Generator
Builds the Graph API request, JSON payload and a Python equivalent for sending an approved template with up to three body variables.
Developer ToolsWebhook Event Test Case Worksheet
A test plan grid for messaging webhooks: inbound text, status receipts, duplicates, bad signatures and ERP failures. Exports to Excel.
More Developer Tools tools
See all- SAP Sales Order ABAP SELECT Generator (VBAK, VBAP, VBFA)
- SAP Sales Order Create BAPI ABAP Generator (BAPI_SALESORDER_CREATEFROMDAT2)
- SAP ABAP Open Items SELECT Generator
- SAP ACDOCA SQL Query Generator
- SAP QM Inspection Lot ABAP SELECT Generator
- SAP Quality Notification Pareto SQL Query Generator
- SAP QM Usage Decision BAPI Skeleton
- ABAP Custom Exception Class Generator
- ABAP Unit Test Class Generator
Keep exploring
Free tools
- Messaging Channel Cost Comparison WorksheetAn editable worksheet that computes spend and true cost per delivered message across channels, including delivery rate.
- Excel to SQL INSERT Statement GeneratorBuild INSERT statements from spreadsheet rows, with an Excel formula that creates the value lists and a rollback-safe wrapper.
- Webhook Event Test Case WorksheetA test plan grid for messaging webhooks: inbound text, status receipts, duplicates, bad signatures and ERP failures. Exports to Excel.
ERP fixes and how-tos
- Event-Driven ERP Integration: Architecture That Holds UpHow to publish ERP changes as events: native event sources, CDC and the outbox pattern, with idempotency, ordering and failure handling for CTOs.
- How the SyteLine Event System Works for Workflow NotificationsHow SyteLine's Event Manager triggers email alerts, Mongoose scripts and ION Workflow off IDO methods: setup steps, Before/After timing, and troubleshooting.
- How to Set Up Business Events and Actions in IFSConfigure IFS Business Events and Actions step by step: define triggers on an entity, enable the event, and bind an email, workflow, or webhook action.
Guides
- SyteLine Event System Customization GuideCustomize SyteLine with the event system. Build event handlers, manage triggers, and extend CloudSuite Industrial without modifying base code.
- How to Configure Event Handlers in SyteLineStep-by-step guide to configuring event handlers in Infor SyteLine for automated responses to data changes, user actions, and system events across your ERP environment.
- Infor LN ION API Integration GuideInfor ION API guide for LN: when to use the ION API gateway, BOD messaging or file-based exchange, how OAuth 2.0 access works, and how to secure calls.
Integrations
- How to add WhatsApp to Infor M3How to connect WhatsApp Business to Infor M3: M3 API (MI programs), ION events, template messages, opt-in rules and two-way replies, including on-prem M3 setups.
- How to add WhatsApp to SAP Business OneHow to add WhatsApp to SAP Business One: Service Layer, Cloud API templates, opt-in rules, order status, delivery alerts and two-way replies into documents.