Model or dataset
matiasbattocchia/open-bsp-api avatar
matiasbattocchia/open-bsp-api

OpenBSP API: WhatsApp and Instagram Messaging as a Postgres Table

Open-source WhatsApp + Instagram Business platform

584 stars248 forksTypeScriptUnlicense

At a glance

What is it?
OpenBSP is a self-hostable, multi-tenant messaging backend where receiving and sending a WhatsApp message means reading and inserting a row. The design is coherent but it hands you a database-shaped product, and that shapes who can operate it.
Who is it for?
Adopt OpenBSP if you already run Postgres and Supabase and want WhatsApp and Instagram messaging to be one more table your backend writes to, or if you are building toward Meta Business Partner status and want the multi-tenant plumbing rather than a single-tenant bot. Do not adopt it if you want a managed vendor with a support contract, a phone number provisioned in minutes, or an SLA attached to message delivery.
Can I use it commercially?
Yes. Unlicense is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 6 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What OpenBSP actually replaces

The README frames the project as an open-source WhatsApp and Instagram Business platform, self-hostable, multi-tenant and AI-agent ready. The concrete problem it addresses is narrower than that framing suggests. Meta's Business messaging APIs give you a phone number ID, a webhook, and a send endpoint. What they do not give you is a conversation store, a per-organization boundary, or a way to hand the same number to several tenants. OpenBSP supplies that middle layer: it receives webhook traffic from Meta, writes each message into Postgres, and dispatches outbound messages you insert into the same table. The README describes two audiences. Individual businesses use it to manage their own WhatsApp and Instagram messaging. Service providers use it as the foundation for becoming a Meta Business Partner and offering messaging to other organizations. The second audience explains the multi-tenant schema: every row carries an organization_id, and the quickstart's curl examples require you to pass one. If you are a single team sending notifications to your own customers, that column is overhead you will carry anyway.

The message table is the API, and PostgREST is the transport

There is no bespoke REST surface here. The README states that the API is PostgREST over the database, every table is an endpoint, and you can use plain REST or any Supabase SDK. That single sentence determines most of the project's ergonomics. Receiving messages means registering a webhook row through a POST to the webhooks endpoint, with fields for organization_id, table_name, operations and url. OpenBSP then POSTs to your URL with a thin envelope: an entity field naming the table, an action field, and a data object holding the row exactly as stored. The example payload shows the shape of that row: id, organization_id, conversation_id, external_id carrying the wamid prefix, service set to whatsapp, organization_address holding the phone number ID, conversation_address and sender_address holding the contact's number, a content object with version, type, kind and text, a status object keyed by delivery state, and a timestamp. The README gives one rule that matters more than the rest of the schema: reply when data.sender_address is set, because that means the contact authored the message, and it is null on your own outgoing rows. Those outgoing rows also fire the webhook, and with the update operation you also get status changes. Sending is an insert into messages with organization_id, organization_address, conversation_address, service and content. The README shows the same insert twice, once as curl against the REST endpoint and once through the Supabase JS client with createClient and a global api-key header. Both require two headers: apikey, described as the public Supabase key, and api-key, your secret key.

Getting it running: hosted first, self-host later

The quickstart path does not involve deploying anything. You sign up at web.openbsp.dev, connect your WhatsApp number under Integrations then WhatsApp, and create an API key under Settings then API Keys. The curl examples then point at a specific Supabase project URL, nheelwshzbgenpavwhcy.supabase.co, which is the hosted backend rather than something you provisioned. That is the fastest route to a working webhook, and it is worth being explicit that it is not a self-hosted setup even though self-hosting is the headline claim. The README also lists a self-host deployment section and a local development section under Development, alongside an architecture section. Those are the parts to read before you commit, because the hosted quickstart tells you nothing about what running your own instance costs in operational effort. There are two companion integrations documented in the same file. The n8n community node package, n8n-nodes-openbsp, triggers on incoming messages and delivery-status changes and can carry full conversation context so an AI node replies with history; the README says it sends text, media, templates, locations and contacts, and reads conversations, contacts and templates. Separately, a Claude Code plugin is installed with two slash commands, /plugin marketplace add matiasbattocchia/open-bsp-api followed by /plugin install openbsp@matiasbattocchia-open-bsp-api, then configured with /openbsp:config login and /openbsp:config contacts add. The plugin exposes a query tool for contacts, conversations and templates and a reply tool for WhatsApp messages.

Multi-tenant by schema, not by convention

Most self-hosted messaging gateways are single-tenant: one deployment, one number, one set of credentials in environment variables. OpenBSP puts the tenant boundary in the data model instead. The quickstart's webhook registration requires an organization_id, and the message insert requires one too, which means a single deployment can hold rows for several organizations and route webhooks per organization. The README connects this directly to the Meta Business Partner path, linking to Meta's solution providers documentation and describing the project as usable to offer messaging services to other organizations. That is a real architectural commitment, and it is the strongest argument for choosing OpenBSP over a thinner webhook relay. It also means the interesting security questions live in row-level access rather than in application code. The README does not spell out how organization isolation is enforced at the database level, and that is the single most important thing to confirm before putting a second tenant on an instance: whether a leaked api-key scoped to one organization can read rows belonging to another. The documentation supplied here does not answer that, and it should not be assumed either way.

Where the design gets in the way

Database-as-API is efficient until it is not. Every message you send is a row insert, which means your send path inherits your database's availability, connection limits and failure modes. If Postgres is unreachable, you are not sending messages. If you insert a row and the downstream dispatch to Meta fails, the row exists and you need to know what state it is in; the schema's status object with delivery timestamps is the mechanism the README shows, but it does not describe retry semantics, dead-letter handling, or what happens to a row whose dispatch never succeeded. The webhook contract has a related sharp edge. The README states plainly that the webhook fires for your own outgoing rows as well as incoming ones, and that the update operation carries status changes. A naive handler that replies to every event will loop: your outbound message triggers a webhook, and if the handler treats it as incoming, it replies again. The guard is checking data.sender_address for null, and the README says so, but it is a rule you have to implement in every consumer rather than something the platform enforces. There is also the unofficial path. The README lists a WhatsApp Web integration described as an unofficial API alongside the official WhatsApp integration. Those two are not equivalent in stability or in what Meta permits, and the documentation here does not draw the boundary between them. Finally, no releases were retrieved for this repository, so there is no versioned artifact to pin against; you would be tracking the main branch or a commit.

How it compares to Twilio and to a plain webhook relay

The README ships a MIGRATING_FROM_TWILIO.md document, which names the comparison the project itself expects you to make. The difference in approach is structural. Twilio's messaging API is a request-response service with its own console, its own number provisioning, and its own message logs; you call an endpoint and Twilio owns the state. OpenBSP inverts that: Meta owns the channel, your Postgres owns the state, and OpenBSP is the adapter that moves messages between them. If you want to query message history, you write SQL against the messages table rather than calling a vendor's list endpoint. If you want to join messaging data to your own customer records, the foreign key is in the same database. The cost of that inversion is that you now operate the database, the adapter, and the Meta app configuration. A second alternative is writing the Meta webhook integration yourself. That is genuinely not much code for a single tenant with one number, and it is the honest recommendation for anyone whose requirement is one number and a handful of message types. OpenBSP earns its place when you need the multi-tenant schema, the conversation and contact tables, or the n8n and Claude Code integrations, because those are the parts you would otherwise build and maintain.

Maintenance, licence and what Unlicense means here

The repository is licensed under the Unlicense, which the README links to unlicense.org. Unlicense is a public-domain dedication rather than a permissive licence with attribution conditions, so it imposes no attribution or copyleft requirement on derivative work. That is unusually permissive for a project in this space and it matters if you intend to fork OpenBSP into a commercial service. It does not remove any obligation you have to Meta, whose Business messaging terms govern the channel itself and are entirely separate from this repository's licence. This is not legal advice; if you are building a commercial messaging service on a public-domain codebase, the Meta side of that question is the one to have reviewed. On maintenance, the last push recorded is 2026-09-09 and the repository is not archived. No releases were retrieved, so there is no changelog to read and no version number to pin. The practical consequence is that upgrade cost is unknown from the outside: you cannot tell from this material whether the schema is stable, whether migrations are shipped, or how a self-hosted instance moves between commits. The README's self-host deployment and architecture sections are where that would be documented, and they are the first thing to read rather than the quickstart.

Editorial conclusion

Adopt OpenBSP if you already run Postgres and Supabase and want WhatsApp and Instagram messaging to be one more table your backend writes to, or if you are building toward Meta Business Partner status and want the multi-tenant plumbing rather than a single-tenant bot. Do not adopt it if you want a managed vendor with a support contract, a phone number provisioned in minutes, or an SLA attached to message delivery. Before committing, verify three things in the repository: the contents of INTEGRATING.md, which is where third-party onboarding and credential capture are documented; the self-host deployment section, since the quickstart points at a hosted Supabase project rather than a local stack; and how the messages table handles status transitions, because the webhook fires for your own outgoing rows and for updates as well as inserts.

Official sources

  1. Issues
  2. License: Unlicense
  3. matiasbattocchia/open-bsp-api on GitHub
  4. Project website
  5. README
Community notes

Community notes