# Rimbra
> Rimbra is the all-in-one platform to put a product on the internet: app hosting from GitHub, managed PostgreSQL, MySQL and Redis, domains with automatic HTTPS, an Email API and Social notifications, in one account with one bill.
Website: https://rimbra.app · Dashboard: https://app.rimbra.app · Short index: https://rimbra.app/llms.txt
## Questions
### What is Rimbra?
Rimbra is one platform for everything a product needs to go live on the internet: app hosting with deploys from GitHub, managed PostgreSQL, MySQL and Redis databases, domain registration with automatic HTTPS, an Email API for transactional email and broadcasts, Social notifications for messaging users on WhatsApp from your code, plus Functions, S3-compatible Buckets, KV storage, Queues, Tunnels, Sandboxes and Captcha. It all runs in one account, one dashboard and one bill.
### What can I host on Rimbra?
Websites, web apps, APIs and backends. Rimbra detects Next.js, React with Vite, Astro, SvelteKit, Nuxt, Remix, Node servers such as Express, Fastify, NestJS and Hono, Python with Django, Flask or FastAPI, PHP and Laravel, Go, Rust, Ruby, Java, .NET, static sites and any Dockerfile. Every project gets a live HTTPS address on its first deploy, and every push to GitHub redeploys with no downtime.
### Does Rimbra have an Email API?
Yes. The Rimbra Email API sends transactional email such as receipts, sign-in links and alerts from your own domain with one HTTP request. Rimbra gives you the DKIM, SPF and DMARC records and checks them for you, and adds templates, broadcasts and a delivery log that tracks every message from queued to delivered.
### Does Rimbra have a WhatsApp API?
Yes. Rimbra Social notifications sends text, images, videos and documents to your users from your server with one API key, reports delivery, and posts incoming messages to your webhooks with a signature. You link your own WhatsApp numbers by scanning a QR code, or get a number through Rimbra, and your team can read and reply from a shared inbox.
### Can I buy a domain on Rimbra?
Yes. Search for a name, buy it and point it at a project in a few clicks, with DNS and HTTPS set up for you. You can also connect a domain you already own from any registrar by adding the records Rimbra shows.
### Is there a free plan?
Yes. The Free plan needs no card and includes 4 projects, 1 database, 200 build minutes, 50 GB of bandwidth, 1,000 emails and 3,000 WhatsApp and social notifications a month. Paid plans are Launch at $5 a month, Pro at $15 and Scale at $49.
### How is Rimbra different from Vercel, Render, Railway or Heroku?
Those platforms host your code, and you add a database provider, a domain registrar, an email service and a messaging provider on top, each with its own account and bill. Rimbra includes hosting, databases, domains, HTTPS, an Email API and Social notifications in one place, already connected to each other.
### What is Rimbra building next?
Rimbra today is v1. Coming soon, on the same account and bill: Mobile Preview Cloud (Android and iOS preview builds with QR codes and promotion to TestFlight or Google Play, Expo and React Native first), Agent Cloud for running AI agents, database branching, a media cloud for images and video, a design studio, game backends, WhatsApp apps, a template marketplace and hosted plugins. The full list is at rimbra.app/roadmap.
### Can AI coding agents deploy to Rimbra?
Yes. Rimbra runs an MCP server at https://app.rimbra.app/mcp that Claude, Cursor, ChatGPT and other AI tools can connect to with OAuth. An agent can create projects, deploy, read build and runtime logs, set environment variables, create databases and send email or WhatsApp messages, within the role you give it. Agents can't pay for anything.
## Products
### Deploy apps
https://rimbra.app/products/deploy
Bring code from GitHub or a folder on your computer. Rimbra builds it, runs it and gives it a live HTTPS address, usually in about a minute.
- Sources: GitHub, or a folder, ZIP or static site
- Runtimes: Node, Python, PHP, Go, Rust, Ruby, Java, .NET, Docker
- Address: your-project.rimbra.live, .xyz or .click, with HTTPS
- Releases: No downtime between versions
#### From code to a live address
Most products start the same way: some code on a laptop and the need to put it in front of people. Rimbra takes that code as it is. Connect a GitHub repository, or drop in a folder, a ZIP or a static site, and Rimbra works out what it is, installs what it needs, builds it and starts it.
Every project gets a free address from its first deploy: your-project.rimbra.live, .rimbra.xyz or .rimbra.click, your choice, served over HTTPS. When you are ready, put your own domain in front of it. The address never changes when you ship a new version, so links you share keep working.
#### It knows your framework
Rimbra reads your files to decide how to run them. You do not write a config file for the common cases.
| Your files | Runs as |
| --- | --- |
| package.json with next | Next.js: builds, then runs the Next.js server |
| Vite, Astro, SvelteKit or Nuxt config | Builds and serves the output |
| package.json with a start script, like an Express or Fastify app | A Node server, run with npm start |
| index.html | A static site, served as files |
| requirements.txt or pyproject.toml | Python with Uvicorn, Gunicorn or Django |
| composer.json or index.php | PHP |
| go.mod, Cargo.toml, Gemfile, pom.xml | Go, Rust, Ruby or Java |
| .csproj, .sln | .NET |
| Dockerfile | Your container, exactly as you wrote it |
One rule for servers: If your app is a server, listen on the port in the PORT environment variable. Rimbra sets it and routes your address to it.
#### Watch every build
Each deploy streams its log line by line, from installing dependencies to the moment your app answers. If a build stops, the reason is in the last few lines, next to the commit or upload that caused it.
Deployments move through Queued, Building and Ready, and every one is kept, so you can open an older build, read its log and see what it shipped.
#### Releases without downtime
A new version starts next to the one that is running. Traffic moves only once the new version answers, then the old one stops. If a build fails, the previous version keeps serving and your visitors never see a broken release.
#### Settings when you need them
- Root directory, for repositories where the app lives in a subfolder.
- Install, build and start commands, when the defaults are not what you want.
- Environment variables and secrets, added one at a time or imported from a .env file.
- A database connection, added as a variable in one step.
**Does my app need anything special to run?** Only one thing for servers: listen on the port in the PORT environment variable. Static sites and frontends need nothing.
**What happens if a build fails?** The live version keeps serving. The failed deployment shows the error in its log, and you can fix it and push again or choose Redeploy.
**Can I bring my own Dockerfile?** Yes. When a Dockerfile is present, Rimbra builds and runs your container and skips its own detection.
**How many projects can I run?** Free includes 4 projects and 200 build minutes a month. Launch, Pro and Scale raise that to 12, 30 and 100 projects. You can also add single projects to any plan.
### Push to deploy
https://rimbra.app/products/git
Connect GitHub once. Every push to your branch builds and ships a new version, and your live site stays up while it happens.
- Access: A read-only GitHub App
- Trigger: Every push to your branch
- On failure: The live version keeps serving
- Control: Automatic deploys can be turned off
#### Connect once
Rimbra connects to GitHub through a GitHub App. You choose your personal account or an organization, and either every repository or only the ones Rimbra should see. Access is read-only and limited to that list, which you can change any time from your GitHub settings.
Once connected, your repositories are listed in Rimbra. Search, choose Import, and the first deploy starts.
#### What happens when you push
1. You push to the branch the project follows.
2. Rimbra creates a deployment with the commit message and author attached.
3. The build runs and streams its log, just like your first deploy.
4. When the new version answers, traffic moves to it. Nobody sees a half-built release.
#### When a push breaks the build
A failed build never replaces a working one. Your site keeps serving the last good version, and the failed deployment shows exactly where it stopped. Fix it, push again, and Rimbra picks it up.
#### Deploy on your terms
- Turn off Automatic deployments in project Settings to ship only when you choose Redeploy.
- Redeploy after changing environment variables so the running app picks them up.
- Cancel a queued or building deployment if you pushed something by mistake.
- Open any older deployment to see which commit it built.
**Can Rimbra change my code?** No. The GitHub App has read-only access, and only to the repositories you chose.
**My repository is not in the list.** Check the account or organization at the top of the list, then add the repository to the Rimbra app’s access list on GitHub and refresh.
**Do pushes use build minutes?** Yes, each build counts toward your monthly build minutes. If your workspace runs out, new pushes are not built until you add minutes or your next cycle starts.
### Databases
https://rimbra.app/products/databases
Managed PostgreSQL, MySQL and Redis that run next to your apps. Connect one to a project and it shows up as an environment variable.
- Engines: PostgreSQL 16, MySQL 8.4, Redis 7
- Ready in: A few seconds
- Connect: One step, as DATABASE_URL
- Backups: Every night, plus any time you choose
#### A database next to your app
Almost every product needs somewhere to keep its data. Rimbra databases run alongside your projects but outside your app, so a redeploy never touches your data. Pick an engine, connect it with an environment variable, and manage everything else from the same place you deploy.
Running a database yourself means installs, upgrades, disks filling up and backups nobody tested. Here you get a working database in a few clicks, with the tools to look after it built in.
#### Pick an engine
| Engine | Version | Good for |
| --- | --- | --- |
| PostgreSQL | 16 | Most apps. Works with Prisma, Drizzle, Django, Rails and Laravel |
| MySQL | 8.4 | Laravel, WordPress and other PHP apps |
| Redis | 7 | Caching, sessions, queues and rate limits |
#### Connected in one step
Open a project, choose Connect and pick the database. Rimbra adds its connection details as variables and your app reads them on the next deploy. The main variable is DATABASE_URL, or another name if your app expects one.
| Engine | Variables you get |
| --- | --- |
| PostgreSQL | DATABASE_URL, PGHOST, PGPORT, PGUSER, PGPASSWORD, PGDATABASE |
| MySQL | DATABASE_URL, MYSQL_URL and the host, port, user, password and database |
| Redis | REDIS_URL, REDIS_HOST, REDIS_PORT, REDIS_PASSWORD |
#### Look after it without another tool
- Data: browse tables and rows, or Redis keys, and edit them in place.
- Query: run SQL one statement at a time, or Redis commands in a console. Commands that could wipe the server are blocked.
- Backups: a full backup every night, more whenever you like, and restore in one step.
- Overview and Logs: status, CPU and memory, connected projects and the server log.
Restores you can undo: Before a restore, Rimbra takes a safety backup of the current data, so you can go back if you picked the wrong one.
#### Your data stays where it is
Because the database is separate from your app, you can redeploy, roll forward or delete a project and the data stays put. Disconnecting a database removes the variable, not the data.
**Can I connect from TablePlus or DBeaver?** Public connections from outside Rimbra are not open by default. Your apps on Rimbra connect directly, and the Data and Query tabs cover browsing and queries.
**How many databases do I get?** Free includes 1. Launch includes 5, Pro 10 and Scale 30.
**Does deleting a project delete its database?** No. Databases are their own resources. You delete one only from its own Settings.
### Domains
https://rimbra.app/products/domains
Search for a name, buy it in seconds and point it at your project. Or bring a domain you already own from any registrar.
- Buy: Search, see the price and renewal, register instantly
- Bring your own: Namecheap, GoDaddy, Cloudflare or any registrar
- DNS: Managed for you on domains bought here
- HTTPS: Issued and renewed automatically
#### A name people remember
Your product deserves an address of its own. Search for a name in the dashboard and Rimbra shows whether it is available, what it costs for the first year and what it renews at. Names that are taken or premium are marked, so you never get to checkout and find out.
Buy it and it is registered right away. Rimbra manages its DNS and points it at your project within minutes, with nothing for you to set.
#### Buy one here
1. Open Hosting → Domains and choose Buy domain.
2. Type a name, with or without an ending, like yourcompany or yourcompany.com.
3. Choose Buy, enter the owner’s contact details and pick add-ons such as WHOIS privacy.
4. Confirm. The domain is yours and its page shows the renewal date.
#### Bring one you already own
Add the hostname to your project and Rimbra shows the one record to set at your registrar, with a copy button.
| Domain | Type | Name | Value |
| --- | --- | --- | --- |
| yourcompany.com | A | @ | The IP shown on the domain page |
| app.yourcompany.com | CNAME | app | cname.rimbra.app |
#### Checks you can see
The domain page checks DNS every 20 seconds and shows three things: whether the domain points at Rimbra, whether its certificate is issued, and whether your project answers on it. When something is off, it says what, in plain words.
#### Move it, or send email from it
- Move a domain to another project from its page. It always serves that project’s latest live version, so redeploys never need a DNS change.
- Add the same domain in Emails → Domains to send mail from it.
**How long until my domain works?** Usually a few minutes. While DNS spreads it can take up to an hour.
**I use Cloudflare.** Set the record to DNS only, the grey cloud. With the proxy on, Rimbra can’t see the record or issue the certificate.
**How many custom domains can I connect?** Free includes 1. Launch includes 10, Pro 25 and Scale 100.
### Automatic HTTPS
https://rimbra.app/products/https
Every address on Rimbra is served over HTTPS. Certificates are issued as soon as your domain points at us and renewed before they expire.
- Cost: Free on every plan
- Issued: As soon as DNS is found
- Renewal: Automatic
- Covers: Free Rimbra addresses and your own domains
#### Secure from the first deploy
Browsers warn people away from sites without HTTPS, and search engines rank them lower. On Rimbra you never think about it. Your your-project.rimbra.live address is served over HTTPS the moment your first deploy is live.
#### Your own domain gets the same
When you connect a domain, Rimbra watches for its DNS record. The moment it is found, a certificate is issued for it, and it renews on its own from then on. There is nothing to upload, no expiry date to remember and no extra cost.
#### Three checks, in plain sight
| Check | What it means |
| --- | --- |
| DNS | The domain points at Rimbra |
| HTTPS certificate | A certificate is issued and renews on its own |
| Site | Your project answers on the domain |
#### When a domain isn’t going live
- Points elsewhere: an old A, AAAA or CNAME record is still there. Delete it.
- Not set up yet: the record isn’t visible yet. Check the name and value, then give it a few minutes.
- Certificate issuing: DNS is right and the certificate is on its way. On Cloudflare, switch the record to DNS only.
- Site not responding: the domain works, but the project isn’t live. Check its latest deployment.
**Do I need to buy or upload a certificate?** No. Certificates are issued and renewed for you, free, on every plan.
**Will a redeploy break HTTPS?** No. The domain and its certificate stay attached to the project and serve each new version as it goes live.
**My domain was bought on Rimbra and stopped working.** Open its Settings and choose Re-apply DNS. Rimbra manages DNS for domains bought here.
### Captcha
https://rimbra.app/products/captcha
Keep bots out of your sign-up, login and contact forms. Visitors click once and carry on, with no tracking cookies.
- For people: One click, usually under a second
- For bots: A proof of work, harder for anyone asking often
- Privacy: No tracking cookies
- Strong: Adds one puzzle piece to slide into place
- Cost: Included on every plan
#### A human check without the puzzles
Forms on the open web fill up with fake sign-ups, stuffed logins and spam. The usual fix makes real people squint at blurry pictures. Rimbra Captcha asks for one click instead.
Behind that click the browser solves a small piece of work that takes a person a fraction of a second and makes running thousands of fake submissions slow and expensive. Visitors asking unusually often get a harder piece of work, and browsers that announce they are automated are refused.
For forms that draw determined bots, set the key to Strong. Visitors then also slide one puzzle piece into a picture. We draw the picture on our servers, so the answer never reaches the page, and we check the drag itself, not just where it ends.
#### Two lines on the page, one call on the server
1. Create a site key in the dashboard and list the hostnames it runs on.
2. Add the script and a box to your form:
.
3. Your form now sends rimbra-captcha-response. On your server, post it with your secret to siteverify before trusting the form.
Already using another captcha?: siteverify answers with success, challenge_ts, hostname and error-codes, the same shape most captcha services use, so switching is usually a URL and a field name.
#### Checkbox or invisible
| Style | What visitors see |
| --- | --- |
| Checkbox | A small box to tick. Most people pass in under a second. |
| Invisible | Nothing. The check runs when the form is sent, then the form goes through. |
#### Built to be hard to fake
- Tokens are signed, tied to your site key and the page’s hostname, work once and expire after 5 minutes.
- A key only works on the hostnames you list, so it can’t be lifted onto someone else’s site.
- Secrets are stored encrypted and can be replaced at any time.
- Test keys that always pass or always fail, for local development and CI.
**Does it set cookies or track visitors?** No. The check needs no cookies and Rimbra keeps no profile of the people who pass it.
**What about people using screen readers or keyboards?** The checkbox is a real button you can reach with Tab and press with Space or Enter, and its result is announced.
**Can it run on localhost?** Yes. Add localhost to the key’s hostnames, or use the test keys, which run anywhere.
### Functions
https://rimbra.app/products/functions
Small JavaScript or TypeScript handlers that answer HTTP requests at their own address or run in front of your app. Warm from the first request, with logs and instant rollback.
- Language: JavaScript or TypeScript, written in the dashboard or taken from your repository
- Where:
.rimbra.live, or paths of your apps like /api/*
#### One file, one address
A function is a module with a fetch method: it gets a Request and returns a Response. Write it in the dashboard editor, deploy, and it answers at its own address a moment later.
Use one for a webhook receiver, a small JSON API, a redirect service or a form handler, without a project, a build or a server to look after.
#### In front of your app
Route a function onto paths of a project, like /api/* or /admin. It sees each request first and either answers it or lets it through to your app with env.next(), so checks like API keys, redirects or header changes live outside the app.
#### Built to stay fast and contained
- Each workspace’s functions run in their own sandbox, separate from every other workspace, with the same isolation as your apps.
- Functions stay warm, so there is no cold start to wait for.
- Every request has a CPU time budget, 128 MB of memory and 30 seconds to answer. A function past a limit is stopped and the visitor gets a clear error.
- Variables and secrets, encrypted at rest, reach the code as env.NAME.
#### See what it does
- Console output and the last 100 requests with status, time and CPU time.
- Requests, errors, p50 and p95 for today and the last 14 days.
- The last 10 versions, each one a click from being live again.
**Can I use npm packages?** Not yet. A function is one file for now; Request, Response, fetch, crypto and the node: built-ins are available.
**What happens past the included requests?** With pay as you go on, extra requests cost $1 per million from your points. With it off, or with no points left, requests get a 429 with a JSON body that says why until the next cycle.
**Does CPU time include waiting on a database or fetch?** No. Only time your code spends working counts.
### Queues
https://rimbra.app/products/queues
Hand slow work to a queue and answer your users straight away. Rimbra stores every message, delivers it to your code and tries again when something fails.
- Delivery: Pushed to your HTTPS endpoint, or pulled by your workers
- Retries: Exponential backoff, then failed messages you can replay
- Guarantee: At least once, stored on disk until delivered
#### Do the slow part later
Sending email, resizing images, calling a payment provider or syncing to another service can take seconds, and can fail. Put that work in a queue and your request finishes at once.
Rimbra keeps each message on disk until your code has handled it. If your endpoint is down or answers with an error, the message waits and is sent again, a little later each time.
#### Send, receive, done
1. Create a queue in the dashboard and point it at one of your apps, or any HTTPS address.
2. Send messages with one POST and a Queues API token: JSON or text, up to 128 KB, one at a time or a hundred at once.
3. Rimbra posts them to your endpoint in batches, signed with the queue’s secret. Answer 2xx and they’re done.
No public endpoint?: Switch the queue to pull. Your workers lease messages over the API and ack them when finished.
#### When something fails
- Each queue sets its attempts (up to 20) and the wait before the first retry. The wait doubles each time, up to an hour.
- After the last attempt a message moves to failed messages. Look at the error, fix your code, then replay one or all of them.
- Your endpoint can finish part of a batch and ask for the rest again.
- Messages can be delayed by up to 12 hours, and a dedupe id stops the same message being queued twice within a day.
**Can a message arrive twice?** Yes, rarely. Delivery is at least once: if your endpoint handles a message but its answer is lost, Rimbra sends it again. Each message keeps the same id across retries, so you can skip ones you have already done.
**Are messages delivered in order?** Roughly oldest first, but not strictly. Retries and parallel batches can change the order.
**How do I know a delivery came from Rimbra?** Every delivery carries a Rimbra-Signature header: an HMAC-SHA256 of the timestamp and body made with the queue’s signing secret. Check it before you trust the body.
**Does queue data count toward storage?** Yes. Messages waiting in your queues count toward your plan’s storage, and are removed once delivered.
### Tunnels
https://rimbra.app/products/tunnels
Put a server on your computer at a public HTTPS address with one command. For webhooks, demos and trying your app on your phone before you deploy.
- Command: rimbra tunnel 3000
- Address: tunnel-xxxx.rimbra.live, with HTTPS
- Passes through: Requests, streams, server-sent events, websockets
#### Your laptop, reachable for a while
Some work needs a public address before it is ready to deploy: a payment provider calling your webhook, a teammate looking at a branch, your own phone opening the page. Rimbra Tunnels gives a server on your computer an HTTPS address on rimbra.live for as long as the command runs.
The CLI opens one outgoing connection to Rimbra, so there is nothing to open on your router or firewall. Each visit travels down that connection to the port you named and the answer comes back the same way.
#### Three commands
1. Install the CLI: curl -fsSL https://rimbra.app/install.sh | sh
2. Make a tunnel token under Tunnels in the dashboard, then run rimbra login and paste it.
3. Run rimbra tunnel 3000 with the port your app listens on. The address prints at once, and each request is listed as it arrives.
A fixed address: With the Tunnels add-on, rimbra tunnel 3000 --name api keeps the same address every time: api-.rimbra.live. Handy for webhook settings you only want to enter once.
#### Yours to switch off
- Tokens are shown once and stored only as a hash. Revoke one and its tunnels close at once.
- Add --auth user:pass and visitors sign in before anything reaches your machine.
- Stop any tunnel from the dashboard. Tunnels with no requests for 4 hours close on their own.
- A tunnel can never take the address of a live app.
- Request bodies up to 32 MB, and your server has 60 seconds to start answering.
**Does it work with websockets and hot reload?** Yes. Websockets, server-sent events and streamed responses pass straight through.
**What does my app see as the Host?** localhost:, so dev servers that check the host accept it. The public address is in X-Forwarded-Host, or pass --host-header preserve to keep it as Host.
**Is my laptop exposed to the internet?** Only the one port you name, only while the command runs, and only through the tunnel address. Nothing is opened on your network.
### KV storage
https://rimbra.app/products/kv
A fast key-value store for config, sessions, feature flags and cached answers. Read and write from any app with one HTTPS request.
- Values: Up to 25 MB each, text or binary
- Expiry: Optional, per key, from 60 seconds
- Reads: Always see the latest write in the region
- Cost: Free plan included; counts toward your plan storage
#### Small data your apps read often
Not everything belongs in a database table. Feature flags, user sessions, rate-limit counters, settings and the answer to a slow API call are easier as a key and a value.
KV storage gives each workspace namespaces of keys, reachable from any app, script or function over HTTPS with a token. There is nothing to run or connect.
#### Three steps
1. Create a namespace in the dashboard.
2. Create a KV token and add it to your project as a secret variable.
3. PUT a value to /values/{key}, GET it back, list keys by prefix, or send many writes at once to /bulk.
Browse it in the dashboard: Search keys by prefix, read values as text, JSON or hex, edit small values and set when they expire, without writing code.
#### Limits by plan
| Plan | Namespaces | Reads a day | Writes a day |
| --- | --- | --- | --- |
| Free | 1 | 100,000 | 1,000 |
| Launch | 10 | 1 million | 1 million |
| Pro | 50 | 10 million | 10 million |
| Scale | 200 | 100 million | 100 million |
#### Safe by default
- Tokens are shown once. Rimbra keeps only a hash and the last few characters.
- The API answers only on api.rimbra.app, never on your sites’ own addresses.
- Bulk writes land all at once or not at all.
**Is it eventually consistent?** No. KV runs in the same region as your apps, and a read right after a write sees the new value.
**What happens when a key expires?** It reads as missing straight away and is cleared from storage within minutes.
**Does it count toward my storage?** Yes. Keys, values and metadata count toward the one storage allowance on your plan, together with project files and databases.
### Buckets
https://rimbra.app/products/buckets
S3-compatible storage for user uploads, media, exports and backups. Files stay put when you redeploy, and your framework’s S3 library already knows how to use it.
- Works with: django-storages, boto3, the AWS CLI, @aws-sdk/client-s3
- Files: Up to 5 GB each, multipart included
- Access: Private with signed links, or public
#### Uploads that survive a deploy
An app’s own folder is rebuilt on every deploy, so profile pictures, attachments and generated files saved there vanish the next time you push. A bucket keeps them apart from your code.
Buckets speak the S3 API, so there is no new library to learn: point django-storages, boto3, the AWS CLI or the AWS SDK at https://s3.rimbra.app with a Rimbra access key.
#### Three steps
1. Create a bucket in the dashboard.
2. Create an access key for all buckets or only this one, read and write or read only.
3. Add the endpoint, region auto and the key to your app’s settings. Django needs eight lines in settings.py.
A file browser in the dashboard: Drop files or folders to upload, preview images, PDFs and text, rename, move and delete, and make links that expire after an hour, a day or a week.
#### Safe by default
- Buckets are private until you make one public. Private files need a key or a signed link that expires.
- Secrets are shown once and stored encrypted. Keys can be limited to some buckets and to reading.
- Files are served sandboxed, so an uploaded HTML or SVG file can’t run scripts.
**Do I need to change my code?** Usually only settings. Any S3 client works once it uses the endpoint https://s3.rimbra.app, region auto and path-style addresses.
**Can browsers upload straight to a bucket?** Yes. Make a presigned PUT URL on your server and add your site under the bucket’s CORS origins.
**What does it cost past the allowance?** $0.03 per GB-month stored, $6.00 per million writes and $0.50 per million reads, paid from your balance. Downloads are free and don’t count toward your bandwidth.
**What if my balance runs out?** Uploads pause until you top up. Files stay readable for 30 days, and we email you before it happens.
**What if I cancel the add-on?** Reads and writes stop at once, but nothing is deleted. Add Buckets again and your files are back straight away.
### Email API
https://rimbra.app/products/email-api
Send receipts, sign-in links and alerts from your own domain with one HTTP request. Every message is tracked from queued to delivered.
- Endpoint: One POST request per email
- From: Any address at a verified domain
- Keys: Send only, Full access or Read only
- Test mode: Test keys log without delivering
#### One request per email
The emails your product sends matter more than most of its pages: the sign-in link, the receipt, the password reset. Rimbra sends them from your own domain with a single request from your server, and records each one so you can see what happened to it.
Send a subject and body, or a saved template with its values. The response comes back straight away with the message ID and the status queued, and the email log follows it from there.
#### What you send
| Field | What it is |
| --- | --- |
| from | An address at a verified domain. "Name " works too |
| to | One address, or a list |
| subject | The subject line, unless you send a template |
| html and text | The body. Send both for the best inbox placement |
| template_id | A saved template, instead of subject and body |
| variables | The values for the template’s {{variables}} |
#### A key for each app
Create keys in Emails → API Keys and name each after the app that uses it. Pick Send only for services that just send, Full access for tools that also read the log, or Read only for dashboards. Revoke one at any time without touching the rest.
Build safely: Test keys accept requests and log them, but never deliver. Build against a test key, then switch to Live when you ship.
#### Answers you can act on
| Code | Meaning |
| --- | --- |
| 202 | Accepted and queued |
| 400 | A field is missing or invalid |
| 401 | The key is missing, wrong or revoked |
| 403 | The key is read only |
| 422 | Test key, unverified from address, unknown template or missing variable |
| 429 | Over 60 sends a minute for this key |
**Can I call it from the browser?** No. Keep keys on your server in an environment variable. Anyone who sees a key can send as you.
**How many emails can I send?** Free includes 1,000 emails a month, Launch 10,000, Pro 30,000 and Scale 100,000, with no daily limit. Add-ons add 500 at a time.
**Which address can I send from?** Any address at a domain you verified, like hello@, receipts@ or no-reply@.
### Sending domains
https://rimbra.app/products/sending-domains
Prove you own your domain and inboxes trust your mail. Rimbra gives you the exact records, checks them for you and tells you the moment you are verified.
- Records: DKIM, return path, SPF and DMARC
- Checks: Every 20 seconds, on their own
- Addresses: Any address at a verified domain
- Works with: Domains bought here or anywhere else
#### Why verification matters
Gmail, Outlook and every other inbox ask the same question about each email: is this really from who it says? A verified domain answers yes. Each message is signed with a key unique to your domain, bounces come back to you, and the “via” label disappears.
Skip it and your mail lands in spam, or is not accepted at all. That is why Rimbra only sends from domains you have verified.
#### The records
| Record | Type | Name | Why |
| --- | --- | --- | --- |
| DKIM | TXT | sb._domainkey | Required. Signs every email |
| Return path | MX | mail | Bounces come back to your domain |
| Return path SPF | TXT | mail | Removes the “via” label in Gmail |
| DMARC | TXT | _dmarc | Recommended. Protects your domain from spoofing |
#### Add, copy, done
1. Open Emails → Domains and choose Add domain.
2. Click any name or value to copy it, and add each record where your DNS is managed.
3. Leave the page open or come back later. It re-checks every 20 seconds.
4. When DKIM is found, the domain shows Verified and you can send.
#### Tips that save an afternoon
- On Cloudflare, keep every email record on DNS only, the grey cloud.
- A name can only have one SPF record. If you already have one, merge them.
- Some registrars add your domain to the name for you. Enter sb._domainkey, not sb._domainkey.yourcompany.com.
**Do I need DMARC?** It is not required to send, but it is recommended. It stops other people sending mail that pretends to be you.
**How long does verification take?** As long as your DNS takes to update, usually minutes. The page checks on its own and updates when it finds the records.
**Can I use a domain I bought on Rimbra?** Yes, and any domain you own elsewhere.
### Templates
https://rimbra.app/products/templates
Write an email once and send it with a different name, amount or link every time. Your code sends the template and the values, Rimbra does the rest.
- Variables: {{double braces}} in the subject and body
- Formats: HTML or plain text
- Safety: Values are escaped in HTML
- Used by: The API and broadcasts
#### Write it once
Receipts, welcome emails and reminders look the same every time except for a few details. Templates keep the design and the words in one place, and leave gaps for the details. Your code stays short, and anyone on the team can fix a typo without a deploy.
#### Create a template
1. Open Emails → Templates and choose New template.
2. Write a subject and a body in HTML or plain text.
3. Wrap the parts that change in double braces, like {{name}} or {{amount}}.
4. Fill in the preview values to see how it looks, then save and copy its ID.
#### Send it from your app
Send the template ID and the values to the same endpoint you use for any email. If a value is missing, the request is rejected and nothing is sent, so a customer never gets an email that says “Hi {{name}}”.
#### Good to know
- Values are escaped in HTML templates, so user input can’t inject markup.
- Editing a template keeps its ID. Duplicate it to start a new one.
- Variable names can use letters, numbers, dots, dashes and underscores.
- Broadcasts can start from a template too.
**Do I need to redeploy after editing a template?** No. The next email sent with that ID uses the new version.
**What happens if I delete a template?** Requests that use its ID fail, so update your app first.
### Broadcasts
https://rimbra.app/products/broadcasts
Send one email, like a product update or a newsletter, to a list of people. Write it, preview it and send it from your own domain.
- Recipients: Paste a list, duplicates removed
- Content: Write it, or start from a template
- Drafts: Save and finish later
- Tracking: Every recipient in the email log
#### News for the people who asked for it
A launch, a new feature, a change to your terms: some emails go to everyone at once. Broadcasts send them from the same verified domain as your product emails, so they look like they come from you, because they do.
#### Write a broadcast
1. Open Emails → Broadcasts and choose New broadcast.
2. Pick the from address from your verified domains.
3. Paste the recipients, separated by new lines, commas or spaces.
4. Write the subject and message, or start from a template. The preview updates as you type.
5. Save a draft, or send.
#### What happens when you send
Rimbra asks you to confirm, then queues one email per recipient. Each appears in the email log with its own status, so you can see who it reached. Each recipient counts as one email in your monthly allowance.
Only email people who asked: Sending to bought or scraped lists hurts your domain’s reputation and can send your mail to spam for everyone.
**Can I edit a broadcast after sending it?** No. A sent broadcast can’t be edited or unsent, which is why Rimbra asks you to confirm first.
**Does every recipient count toward my emails?** Yes. A broadcast to 300 people uses 300 of your monthly emails.
### Email log and metrics
https://rimbra.app/products/email-log
Every message you send, with its status and the reason when one fails. Plus your delivery rate and volume at a glance.
- Statuses: Queued, sending, delivered, failed
- Search: By recipient, sender or subject
- Export: CSV of what you see
- Metrics: Over 7, 15 or 30 days
#### Know what happened to every email
“I never got the email” is a support ticket every product gets. The email log answers it in seconds. Search for the address, open the message, and see when it was sent, whether it was delivered and, if not, why.
#### Statuses
| Status | Meaning |
| --- | --- |
| Queued | Accepted and waiting to send |
| Sending | Being handed to the recipient’s mail server |
| Delivered | The recipient’s mail server accepted it |
| Failed | Couldn’t be delivered. The message shows the reason |
#### Find a message fast
- Search by recipient, sender or subject.
- Filter by status or date.
- Export what you are looking at as CSV.
- Open a message for its full details.
#### Metrics
Metrics shows your delivery rate, daily volume and a breakdown by sending domain over 7, 15 or 30 days, so a problem with one domain stands out. The log and metrics cover your most recent 50 messages.
**An email failed. What now?** Open it and read the reason. A rejected from address means its domain isn’t verified; a missing mailbox means the address doesn’t exist and should come off your list.
**Do test key emails show up?** Yes. They are logged so you can check your integration, but they are never delivered.
### WhatsApp numbers
https://rimbra.app/products/whatsapp
Link your own WhatsApp numbers with a QR code and message your users from the dashboard or your code. No new number to buy.
- Linking: A QR code, or an 8-character code
- Numbers: Link several and pick one per message
- Allowance: Monthly, with no daily limit
- Opt-outs: STOP handled for you
#### Talk to users where they already are
For a lot of people, WhatsApp is where messages actually get read. Order updates, appointment reminders and sign-in codes land next to their conversations with friends, not in a folder they never open.
Rimbra uses the numbers you already have. Link one the same way you link WhatsApp Web, and send from it in the dashboard or through the API.
#### Link a number
1. Open Social notifications → WhatsApp and choose Activate WhatsApp. Your API key is created for you.
2. Open Numbers and choose Link a number.
3. On your phone, open WhatsApp → Settings → Linked devices and scan the QR code. Or type the 8-character code instead.
4. The number shows Connected and stays linked.
#### Many numbers, one workspace
Link a number for support and another for alerts. Rename them so you know which is which, see how many messages each sent today, and choose which one sends each message. Your allowance is shared across all of them.
#### Keep your numbers healthy
Write numbers with the country code, or let Rimbra add the code of the number you send from. Contacts who reply STOP or UNSUBSCRIBE are opted out on their own, and messages to them are refused.
Message people who expect it: WhatsApp can restrict numbers that get reported. Keep messages useful and wanted.
**Do I need a WhatsApp Business API account?** No. You link a number you already use, the same way you link WhatsApp on a computer.
**How many messages can I send?** Your plan includes a monthly allowance of social notifications: 3,000 on Free, 10,000 on Launch, 25,000 on Pro and 100,000 on Scale. You can use it at any pace; there is no daily limit.
**What if the phone logs the number out?** The number shows as disconnected and a number.disconnected webhook is sent. Link it again from Numbers.
### Shared inbox
https://rimbra.app/products/inbox
Every WhatsApp conversation from every number you linked, in one place. Read, reply and keep track of contacts without picking up the phone.
- Threads: Every conversation, every number
- Reply: From the dashboard
- Contacts: Names and opt-outs
- Compose: Start a new conversation
#### One inbox for all your numbers
When people reply to your notifications, those replies should not be stuck on one phone in someone’s pocket. The inbox brings every conversation from every linked number into the dashboard, newest first.
#### Reply and compose
- Open a conversation to see the whole thread and reply from the same number.
- Use Compose to message someone new from any of your numbers.
- Messages you send from the dashboard appear in the thread like any other.
#### Contacts
Give contacts names so threads are easy to find. Anyone who replies STOP or UNSUBSCRIBE is opted out automatically, and you can turn messaging back on for them in Contacts if they ask.
#### Also from your code
The same conversations are available through the API, and the message.received webhook tells your server about each new message as it arrives, so your product can react to replies too.
**Do replies from the inbox use my allowance?** Messages you send count toward your monthly social notifications, whether they come from the inbox, Compose or the API.
**Can my app read replies?** Yes. Use the inbox endpoints, or subscribe to the message.received webhook.
### Social notifications
https://rimbra.app/products/whatsapp-api
Send text, images, videos and documents to your users from your server, check delivery and read conversations, all with one key.
- Messages: Text, image, video, audio and documents
- Sender: Pick a number, or use the latest
- Status: Queued, then sent or failed in seconds
- Key: Rotate it any time
#### Messages from your product
Your shipping service tells the customer their order left. Your booking app reminds them an hour before. Your checkout sends the invoice as a PDF. Each is one request with the recipient and the content. The response is the message, queued, and it becomes sent or failed within seconds.
#### What you can send
- Text, up to the length WhatsApp allows.
- Media from any https link Rimbra can download: image, video, audio or document. The text becomes the caption.
- From a specific number, by passing its ID, or from your most recently connected number.
#### Endpoints
| Endpoint | Does |
| --- | --- |
| POST /messages | Send a text or media message |
| GET /messages/{id} | One message and its status |
| GET /whatsapp/numbers | Your numbers and how many they sent today |
| GET /inbox/threads | Conversations and their messages |
| GET /contacts | Contact names and opt-outs |
| GET /usage | Daily sent, received and failed |
| /webhooks | Manage webhook endpoints |
#### Errors that explain themselves
| Code | Meaning |
| --- | --- |
| 401 | The key is missing or wrong |
| 422 | The recipient isn’t on WhatsApp or has opted out |
| 429 | Your workspace used its monthly allowance. Add more in Billing → Usage |
**Where do I get a key?** Activating WhatsApp creates one. It lives in WhatsApp → API, where you can also rotate it. The old key stops working right away.
**Can I call the API from a mobile app?** No. Call it from your server so the key stays secret.
### Webhooks
https://rimbra.app/products/webhooks
Get new messages, delivery results and number changes on your server the moment they happen, signed so you know they came from Rimbra.
- Events: Messages, deliveries and number changes
- Signing: HMAC-SHA256 with your secret
- Retries: Up to three, waiting longer each time
- Testing: Send a test event from the dashboard
#### Events
| Event | Sent when |
| --- | --- |
| message.received | Someone messages one of your numbers |
| message.sent | Your message was handed to WhatsApp |
| message.failed | A message couldn’t be sent, with the reason |
| number.connected | A number finished linking |
| number.disconnected | A number was logged out from the phone |
#### Add an endpoint
1. Open WhatsApp → Webhooks and choose Add endpoint.
2. Enter your https URL and pick the events you want.
3. Copy the signing secret.
4. Send a test event and check Recent deliveries.
#### Signed, so you can trust it
Every request carries the event name, a timestamp and a signature made with your secret. Your server checks the signature and the timestamp before acting, so nobody else can fake an event or replay an old one.
#### Retries and pauses
- Answer with a 2xx status within 10 seconds.
- Failed deliveries are retried up to three times, waiting longer each time.
- Every attempt shows under Recent deliveries with its response code.
- Pause an endpoint with its switch while you fix your server.
Answer fast: Store the event and return 200 straight away, then do the slow work in the background.
**How do I check a signature?** The docs show the exact steps and a short example for Node.js.
**Can I have more than one endpoint?** Yes. Add as many as you need, each with its own events and secret.
## Docs
### Deploy your first app
https://rimbra.app/docs/quickstart
From a GitHub repository or a folder on your computer to a live HTTPS address in a few minutes.
#### Before you start
- A Rimbra account. Sign up or sign in at app.rimbra.app/sign-in; the Free plan needs no card.
- Your code in a GitHub repository, or a folder or ZIP you can upload.
- If your app is a server, it must listen on the port in the PORT environment variable. Rimbra sets it for you.
#### 1. Create an application
1. In the dashboard, choose New → Application, or open Hosting → Applications and choose New application.
2. Under Source, pick GitHub. The first time, install the Rimbra app on your account or organization, then pick the repository.
3. To deploy files instead, pick Rimbra Drop and drag in a folder, a ZIP or a static site.
4. Choose a region. EU Central suits most apps.
5. Under Set up, check the application name. It becomes your free address. Pick the ending you like: your-app.rimbra.live, .rimbra.xyz or .rimbra.click.
6. Choose Deploy.
#### 2. Watch the build
Rimbra detects the framework, installs dependencies, builds the app and starts it. Every step streams into the deployment log, so you can see exactly where a build is and why it stopped if it fails.
Most apps are live in about a minute. Bigger builds with many dependencies take longer the first time and are faster after that.
#### 3. Open your app
When the deployment shows Ready, choose Visit. Your app is served over HTTPS at your-app.rimbra.live (or the ending you picked).
Push to deploy: Applications imported from GitHub redeploy on every push to the branch you picked. The new version only replaces the old one once it is built and running, so visitors never see a broken release.
#### Next steps
### How Rimbra works
https://rimbra.app/docs/how-it-works
One workspace for hosting, databases, domains, email and WhatsApp, and how the pieces fit together.
#### Your workspace
Everything you create lives in your workspace: applications, databases, domains, email senders, WhatsApp numbers and API keys. Nothing is shared with other accounts, and each product can use the others without extra setup. An application can read a database through an environment variable, serve a domain you bought here, and send email from that same domain.
#### The products
| Product | What it does | Where |
| --- | --- | --- |
| Hosting | Builds and runs your apps from GitHub or uploaded files | Hosting → Applications |
| Databases | Managed PostgreSQL, MySQL and Redis | Hosting → Databases |
| Domains | Buy domains or connect your own, with automatic HTTPS | Hosting → Domains |
| Email API | Transactional email and broadcasts from your own domain | Emails |
| Social notifications | Send and receive WhatsApp messages from your numbers | Social notifications → Numbers |
#### From code to a live app
1. Source: Rimbra pulls your repository from GitHub or unpacks your upload.
2. Detect: it reads your files to work out the framework and how to run it.
3. Build: it installs dependencies and runs the build, logging every line.
4. Run: static sites are served as files; servers start on their own port with your variables.
5. Switch: once the new version answers, traffic moves to it. If anything fails, the previous version keeps serving.
#### APIs and keys
The Email API and Social notifications each have their own API with their own keys, created in the dashboard. Keys belong to your workspace and can be revoked at any time. Call these APIs from your server, never from browser or mobile code.
| API | Base URL | Key from |
| --- | --- | --- |
| Email API | https://api.rimbra.app/api/marketing/emails | Emails → API Keys |
| Social notifications | https://api.rimbra.app/ntf/v1 | Social notifications → API |
#### Coming soon
Sandboxes, the Agent, email automations and brands are on the way. Their pages in the dashboard are marked Coming soon until they are ready.
### Applications
https://rimbra.app/docs/projects
An application is one app: its source, settings, variables, domains and every deployment it has had.
#### Applications and deployments
An application is the app you manage. A deployment is one build of it. Each push, redeploy or upload creates a new deployment, while the application keeps its name, address, settings, variables, domains and database connections.
#### Create an application
1. Open Hosting → Applications and choose New application, or choose New → Application from any page.
2. Under Source, pick GitHub, Rimbra Drop to upload a folder, ZIP or static site, or Docker image.
3. Pick the repository and choose a region.
4. Under Set up, set the application name. It decides your free address, like your-app.rimbra.live. Check the branch, add any variables, then choose Deploy. You follow the first build on the deploy screen.
#### Inside an application
Redeploy and Visit sit at the top of the application. To connect a database, open it under Hosting → Databases and pick the application under Used by.
| Tab | Use it to |
| --- | --- |
| Overview | See the live deployment, its address, traffic and recent deploys |
| Deployments | Browse every build, open its log, redeploy, retry, cancel or promote an earlier build |
| Logs | Read what your app prints while it runs, live or for a time range |
| Analytics | See visitors, page views, top pages, countries, referrers and status codes |
| Variables | Add environment variables and secrets |
| Cron jobs | Call a path on your app on a schedule |
| Domains | Connect your own domain or one bought on Rimbra, and pick the primary domain |
| Security & Speed | Protection, firewall rules, rate limits, caching, image optimisation and the waiting room |
| Settings | General, Build & runtime (commands and root directory), Integrations and Danger zone |
#### Cron jobs in vercel.json or rimbra.json
Declare cron jobs in your repository and Rimbra sets them up on every production deploy. Jobs you remove from the file are removed. A rimbra.json job can run a command instead of calling a path.
```json
{
"crons": [
{ "path": "/api/cron/cleanup", "schedule": "0 5 * * *" }
]
}
```
| Plan | Cron jobs | Runs at most |
| --- | --- | --- |
| Free | 2 | every 10 minutes |
| Launch | 10 | every minute |
| Pro | 25 | every minute |
| Scale | 100 | every minute |
Over the plan: If the file asks for more jobs, or more often, than your plan allows, the deploy stops before building and says what to change. Jobs made in the dashboard count toward the same limit. Previews never run cron jobs.
#### Analytics
Every request to your application is counted at Rimbra's edge. There is no script to add and no cookies. A visitor is counted from a one-way hash of their address, browser and the day. The address is never stored, and the hash is discarded after two days.
The Analytics tab shows visitors, page views, error rate and p95 response time for the last 24 hours, 7, 30 or 90 days, with page views over time, top pages, countries, referrers and status codes.
Bots, crawlers, link previews and probes for files like /.env are kept out of visitor numbers.
| Plan | Analytics history |
| --- | --- |
| Free | 7 days |
| Launch | 30 days |
| Pro | 90 days |
| Scale | 1 year |
Longer ranges: Rimbra keeps a year of daily totals for every application, so after you upgrade you can see traffic from before the upgrade.
#### Delete an application
Open Settings → Danger zone, type the application name and choose Delete application. It takes the address offline and removes every deploy. Databases you connected are kept.
#### Transfer to another account
When they accept, every build, the application’s variables, custom domains and cron jobs move to their account, along with any database only this application uses. The address stays the same and the app restarts once. A database other applications also use stays with you, and the moved application loses its connection to it.
1. Open the application, go to Settings → Danger zone and choose Transfer.
2. Enter the email of the Rimbra account that should own it. They need an account already.
3. They see the offer on their Overview and by email, and have 7 days to accept or decline. You can cancel it before then.
GitHub applications: Live builds keep running. To redeploy from the repository, the new owner connects a GitHub account that can access it.
### Deploy from GitHub
https://rimbra.app/docs/github
Connect GitHub once, import any repository and redeploy on every push.
#### Connect your account
Rimbra uses a GitHub App, so access is read-only and limited to the repositories you chose. You can change that list any time from GitHub → Settings → Applications.
1. Choose New → Application and pick GitHub under Source, or choose Connect GitHub under Settings → Integrations.
2. Pick your personal account or an organization.
3. Choose All repositories or only the ones Rimbra should see, then install.
4. Back in Rimbra, your repositories are listed. Search and choose Import.
#### Push to deploy
Every push to the application branch starts a new deployment with the commit message and author attached. If the build fails, the live version stays up and the failed deployment shows the error in its log.
Turn it off: Disable Automatic deployments in application Settings to deploy only when you choose Redeploy.
#### New repositories
When you push code to a new repository that the Rimbra GitHub App can see, we email you once with a Deploy button. It opens the import with that repository, its branch and a suggested name filled in. We only send it for repositories less than a week old, not forks, with an app we can detect, and not already imported. Turn it off under Settings → Notifications → Product updates.
#### Environments
An environment is a long-lived copy of an application, like staging or dev. Add one from the application's Environments tab: give it a name and pick the branch it follows. It gets its own address, - on the same ending, and every push to that branch deploys it. Production is never touched.
An environment uses production's build settings and variables. Variables you add to the environment win over production's, so staging can use a test database or test keys. Redeploy the environment after changing them.
Each plan includes a number of environments per application, production included: Free 1, Launch 2, Pro 3 and Scale 6. Removing an environment takes its address down. Deleting the application removes its environments too.
Environments or previews: Previews come and go with branches and are made on their own. An environment is one you make on purpose and stays until you remove it, even if its branch is deleted.
#### Deploy from GitLab
Connect GitLab under Settings → Integrations: choose Connect GitLab, approve Rimbra on GitLab and you come back to the dashboard.
Rimbra lists GitLab projects where you are a Maintainer or Owner. Pick one, then the branch and folder, the same as from GitHub. On the first deploy Rimbra adds a push webhook to the GitLab project, so every push to the branch redeploys and other branches get preview deployments when previews are on.
Picking GitLab under Source in New → Application is coming to this dashboard soon. Until then, use GitHub, Rimbra Drop or Docker image there.
Disconnect: Settings → Integrations → GitLab → Disconnect removes the saved sign-in. Applications keep running; pushes stop deploying until you connect again. Delete the Rimbra webhook under the GitLab project's Settings, Webhooks if you no longer want it.
#### Deploy from Bitbucket
Connect Bitbucket under Settings → Integrations: choose Connect Bitbucket, approve Rimbra on Bitbucket and you come back to the dashboard. Picking Bitbucket under Source in New → Application is coming to this dashboard soon.
Rimbra lists Bitbucket Cloud repositories you administer. Pick one, then the branch and folder, the same as from GitHub. On the first deploy Rimbra adds a signed push webhook to the repository, so every push to the branch redeploys and other branches get preview deployments when previews are on.
Disconnect: Settings → Integrations → Bitbucket → Disconnect removes the saved token. Applications keep running; pushes stop deploying until you connect again. Remove the Rimbra webhook under the repository's Repository settings, Webhooks on Bitbucket if you no longer want it.
#### Move apps from Vercel
Migrate from Vercel, under Source in New → Application, is coming to this dashboard soon. It takes a Vercel access token, lists your Vercel projects and rebuilds each one from the repository it is linked to on GitHub, GitLab or Bitbucket, with its build settings, variables and domains. The token is used for that request only and never stored.
A Vercel project linked to GitLab or Bitbucket needs that source connected first. The migration screen offers to connect it in place, then checks again. GitLab projects need Maintainer access and Bitbucket repositories admin access, so the push webhook can be added. Traffic only moves when you press Switch traffic on a domain, after the Rimbra build is live.
#### A repository is missing
1. Check you picked the right account or organization at the top of the list.
2. Open the Rimbra app on GitHub and add the repository to its access list.
3. Return to Rimbra and refresh the list.
### Deployments and logs
https://rimbra.app/docs/deployments
What happens during a build, what each status means and how to fix a failed deploy.
#### Statuses
| Status | Meaning |
| --- | --- |
| Queued | Accepted and waiting for a build slot |
| Building | Installing dependencies, building and starting the app |
| Ready | Live and serving traffic |
| Failed | A step stopped with an error. The previous version keeps serving |
| Cancelled | Stopped before it finished |
When builds are busy: When many builds start at once, they wait their turn. A waiting build shows how many builds are ahead of it. Paid plans build before Free, and within a plan the oldest build goes first. Your live apps, databases and emails never wait behind builds.
#### Runtime logs
The Logs tab shows what your app prints for the last 15 minutes, hour, 24 hours or 7 days. Turn on Live to follow new lines as they arrive, pick Error, Warning, Info or Debug to show only those levels, and search the messages.
#### The release track
The top of an application's Deployments tab shows your latest production builds as bars, newest on the right. A taller bar took longer to build. Neon is the build that's live, red failed and grey finished without going live.
Pick a bar or a row to see that build's message, commit, branch and build time. Each build's menu has Logs, Retry for a failed build, Cancel build while it builds, and Promote (roll back to this) on an earlier working build, as long as its files are still kept. Redeploy and Visit stay at the top of every application tab.
#### Regions
When you deploy from GitHub, you choose where your app builds and runs. The region stays with the application after you deploy. To move an app, deploy it again as a new application.
EU West is a separate server for your app. Visitors and databases still go through EU Central in Frankfurt, so EU West is not faster, and apps that use a Rimbra database are a little quicker in EU Central. A first build also takes about 4 times longer there. Later builds reuse saved packages and are close to EU Central. Choose EU Central unless you want your app on a separate server.
| | EU Central | EU West |
| --- | --- | --- |
| Location | Frankfurt, Germany | Western Europe |
| First build | Fastest | About 4 times slower |
| Rimbra databases | Run next to your app | Connect from EU Central |
| Hosts | Every kind of application | Apps with a server, not static sites |
| Deploys from | GitHub, upload or Docker | GitHub |
| Cron jobs | URL and command jobs | URL jobs |
#### Firewall and rate limits
Each application has its own firewall on the Security & Speed tab: choose Manage next to Firewall rules or Rate limits. Rules run before a request reaches your app, and changes apply straight away.
Free includes 5 firewall rules and 1 rate limit per application, matching by country, IP address, path and user agent. Launch includes 25 rules and 10 rate limits and adds network, method, header and query matches. Pro includes 100 and 50, Scale 500 and 200. After a downgrade, the rules and settings you already have keep working and can be removed; new ones need to fit the new plan.
A rate limit counts requests per visitor IP on a path. Many phone users can share one IP, so keep limits roomy. Visitors over the limit get a 429 with a Retry-After header.
Visitor countries come from IP geolocation by DB-IP. A visitor whose country is unknown is never blocked by a country rule.
| Rule | Example | Matches |
| --- | --- | --- |
| Country | RU, CN | Two-letter country codes |
| IP address | 203.0.113.7 or 203.0.113.0/24 | One address or a range |
| Path | /wp-* | An exact path, or anything starting with the part before * |
| User agent | sqlmap | Any part of the User-Agent header |
| Network | AS16509, AS14061 | The network a visitor connects through, by AS number. Network data by DB-IP |
| Method | DELETE, PUT | The request method |
| Header | X-Api-Key | A request header, optionally containing some text. Empty text means the header is present |
| Query | debug=1 | Any part of the query string, after the ? |
Order: An allow rule beats every block rule and skips rate limits, so you can block a country and still let one office IP in.
#### HSTS and TLS version
On the Security & Speed tab, turn on HSTS. Browsers then remember to load your site over HTTPS only, for a year by default. Include subdomains extends it to every subdomain.
Preloading lets browsers ship your domain on their HTTPS-only list. Use it only on your own domain, once you are sure: browsers take months to forget it. It needs subdomains included and at least a year.
Minimum TLS is 1.2 by default, which works with almost every browser and app. Choose 1.3 to refuse older connections: they get a 426 asking them to update.
#### Response headers and rewrites
Available on Launch and above. On the Security & Speed tab, Headers and rewrites, set or remove a response header on a path, like X-Frame-Options: DENY on /*, or remove X-Powered-By from /api/*. Headers that belong to the connection or carry cookies, like Set-Cookie and Content-Length, can't be changed.
A rewrite serves one path from another without a redirect: /docs/* from /help/* keeps the address in the browser and shows /help/ pages. Rewrites stay on your site. Firewall rules and rate limits look at the path the visitor asked for; saved pages are kept under the rewritten path.
#### Visitor sign-in
Available on Launch and above: Launch lets up to 25 emails or domains in, Pro and Scale have no limit. Turn on Visitor sign-in on the Security tab to put a sign-in page in front of your site. Visitors type their email and get a 6-digit code. Only addresses you list get a code: single emails like ada@company.com, or whole domains like @company.com. You can also let in everyone in your Rimbra workspace, who sign in with their Rimbra account instead.
Protect the whole site, only preview builds, or just some paths like /admin/*. Paths that stay open, such as webhooks other servers call, skip the sign-in.
A signed-in visitor stays signed in for 1, 7 or 30 days on that device. Removing an address from the list ends its sessions on the next request, and Sign everyone out ends all of them at once.
- The page gives the same answer for every address, so nobody can find out who is on the list.
- Codes expire after 10 minutes, work once and allow five tries. Asking for codes is limited per address and per network.
- The sign-in cookie is removed before the request reaches your app, and pages behind the sign-in are never shared through the page cache.
- Codes are sent from noreply@rimbra.app. The Security tab lists recent sign-ins with the address, time and country.
#### Under-attack mode
Available on Launch and above, along with Strict protection. On the Security & Speed tab, set Protection to Under attack when your site is being flooded. Every visitor then passes a quick browser check before reaching your app: the page works out a small puzzle in JavaScript, which takes about a second, and the browser gets a cookie that lasts 24 hours. Scripts and floods that don't run JavaScript never reach your app.
Strict protection watches your normal rate and turns the check on by itself during a spike. When requests jump to ten times that (and past 1,200 a minute), the check runs for 30 minutes and then turns off again. If you set up Slack or Discord alerts, you get a message when it happens.
Requests that aren't from a browser get a 503 with a Retry-After header. Add your API and webhook paths to the paths that skip the check, so mobile apps and other servers keep working. /api/* and /webhooks/* are added for you the first time.
- Google, Bing, Apple, Yandex and Baidu crawlers skip the check after Rimbra confirms them with a reverse and forward DNS lookup.
- An allow rule in the firewall skips the check, and so does Rimbra's own uptime monitor.
- The cookie is removed before the request reaches your app, so it never shows up in your code or stops page caching.
#### Attack rules
Attack rules block common attacks before they reach your code. They are on when Protection is Standard or above, and off when it is Off. Blocked visitors get a 403.
| Group | Blocks |
| --- | --- |
| SQL injection | UNION SELECT, ' OR 1=1, stacked DROP TABLE, sleep() timing tricks, in the path or query string |
| Cross-site scripting | Script tags, event handlers like onerror=, and javascript: links in URLs |
| Path traversal | ../../etc/passwd, php:// and file:// includes |
| Probes for leaked files | /.env, /.git, wp-login.php, xmlrpc.php, phpMyAdmin and similar |
| Scanner tools | sqlmap, Nikto, Nuclei, WPScan and other scanners, by user agent |
WordPress and PHP: Probes for leaked files includes WordPress paths, so it starts switched off when your application is PHP. Turn it off yourself if you run WordPress some other way.
#### Bot controls
Block AI crawlers stops bots that read sites for AI training and AI answers, such as GPTBot, ClaudeBot, PerplexityBot, CCBot and Bytespider. Search engines are not affected.
Block bad bots, available on Launch and above, stops requests with no user agent, scripting tools like python-requests, curl and Scrapy, and SEO crawlers like AhrefsBot and SemrushBot. If you call your own site from a script, add that path to the paths that skip the check, or leave this off.
#### Security events
Security events on the Security tab lists what your firewall, rate limits, attack rules, bot controls and browser check stopped, with counts by reason for today and the last 7 days. Each event shows the time, the path, the reason, the visitor's country and network, and the user agent.
IP addresses are shortened to their network (/24 for IPv4, /48 for IPv6). The last 500 events are kept for each application. During a flood Rimbra keeps a sample, while the counts stay exact.
#### Page caching
Under Speed on the Security & Speed tab, set Caching to Pages too, on every plan. Rimbra then saves finished pages and sends them to the next visitor without running your app. With Files only, pages always come from your app. Every response carries X-Rimbra-Cache: HIT, MISS, STALE, STALE-IF-ERROR or BYPASS, so you can see what happened.
When a saved page has just expired, the visitor gets the saved copy at once and a single background request fetches a fresh one. A deploy starts with an empty cache, and Purge cache empties it.
Text pages are stored compressed, so a busy page isn't compressed again for every visitor.
- Requests with a cookie or Authorization header skip the cache, apart from static files.
- Anything under /api is not cached unless a cache rule says so, and paths you add to Never cache never are.
- Responses that set a cookie, or say private, no-store or no-cache, are never saved.
- If your app sends a shorter s-maxage or max-age, that time wins.
#### Cache rules
Cache rules set caching per path. With Caching on Pages too, choose Add path under Specific paths, enter a path like /blog/* and pick Cache or Never cache. A Cache rule added there uses the page cache time; a rule can also carry its own time, from 10 seconds to 7 days. Rules are checked top to bottom and the first match wins, ahead of the defaults. Free includes 3 cache rules per application, Launch 25, Pro 100 and Scale 500.
A rule can cache an /api route that answers everyone the same, like live scores or a product list, so a traffic spike reads from the cache instead of your app. Requests with cookies still skip it.
On static files only (paths under /assets/, /static/, /_next/static/, /images/, /fonts/ and similar, or a single file like /logo.png), a rule can cache even when the visitor has cookies. The cookies aren't sent to your app for those requests.
| Rule | Example | Effect |
| --- | --- | --- |
| Cache, 10 seconds | /api/scores/* | Everyone shares one copy, refreshed every 10 seconds |
| Cache, 7 days, cookies ignored | /assets/* | Build files are served from the cache for every visitor |
| Never cache | /checkout/* | Always goes to your app |
#### Always Online
With page caching on, Always Online is on by default. If your app answers 500, 502, 503 or 504, or doesn't answer at all, visitors get the last saved copy of the page instead of an error, for up to 24 hours after it expired.
Pages served this way carry X-Rimbra-Cache: STALE-IF-ERROR. Turn off Always Online on the Security & Speed tab if you would rather show the error.
Only saved pages: Always Online can only stand in with pages that were cached before the app failed. Signed-in pages and anything that sets a cookie are never cached, so they still show the error.
#### Waiting room
Available on Launch and above. Turn on Waiting room on the Security & Speed tab to protect your app from traffic spikes, like a ticket sale or a matchday. Set Visitors let in at a time. When the site is full, new visitors wait on a simple page that shows their place in line and an estimate, and checks again every 20 seconds.
By default visitors go in the order they arrived, as active visitors leave. A visitor who is idle for longer than the time you choose gives up their place. Refreshing the waiting page keeps the place in line.
Requests that aren't pages, like fetch calls, get 503 with a Retry-After header until the visitor is let in. Paths you list, like /api/* and /webhooks/*, never wait, so payment webhooks and server-to-server calls keep working.
- You can set the waiting page title and a short message, like when a sale opens.
- The dashboard shows how many visitors are active and how many are waiting.
- Rimbra's own uptime checks skip the line.
#### Random order and scheduled events
On the Pro plan you can let visitors in at random instead of first come. Everyone who arrives in the same minute gets a random place in line, ahead of anyone who arrives later, so nobody waits forever and refreshing early doesn't help. Once someone is let in, they stay in.
A scheduled event has a start and an end, set in your own time zone and stored in UTC. From 15 minutes to 2 hours before the start (30 by default), visitors get an "Opens at" page with the opening time in their own time zone and a place in line. Nobody goes in before the start. At the start, everyone already waiting goes in first, in a random order if random is on.
During the event the waiting room runs even if the queue switch is off, with its own limit and message if you set them. Turn on "Queue everyone" to give every new visitor a place in line during the event, even below the limit, so how fast someone clicks doesn't decide who gets in. The dashboard shows whether the event is scheduled, open for the line, live or over.
#### Waiting room for apps
Mobile apps and single-page apps can read the line as JSON (Pro plan). Turn on "Answer apps in JSON" under More queue settings. A request with Accept: application/json, or with ?format=json, then gets a 503 with Retry-After and a JSON body instead of plain text.
status is queued, prequeue (before an event opens) or full. position is the place in line, estimatedWaitSeconds is 0 while it isn't known yet, and opensAt is set before an event starts.
Apps that keep cookies hold their place automatically. Apps that don't should save the ticket and send it back on every request in an X-Rimbra-Ticket header. When the visitor is let in, the response comes from your app as usual and carries a new X-Rimbra-Ticket value: keep sending that one. Rimbra removes the header before the request reaches your app.
```http
HTTP/1.1 503 Service Unavailable
Retry-After: 20
X-Rimbra-Ticket: cXxzaG9w...
{"status":"queued","position":42,"estimatedWaitSeconds":180,"retryAfter":20,"ticket":"cXxzaG9w..."}
```
#### Image optimisation
With Image optimisation on (Security & Speed tab, every plan), JPEG and PNG files go to browsers that accept WebP as smaller WebP files, at up to 2560 pixels wide. The first visitor gets the original, and the optimised copy is ready after that. If WebP wouldn't be smaller, the original stays.
To resize as well, request /_rimbra/image with the source path and a width. Widths snap to the nearest standard size.
```html
```
#### Redirects
Add redirects under Settings, Redirects, with no code change. A * in the old path carries the rest of the address across, so /blog/* to /news/* keeps every post working. Query strings are kept.
Use Permanent when a page has moved for good, so search engines carry its ranking across. Use Temporary when it will come back.
#### Site-down alerts
Turn these on under Settings, Alerts. Rimbra checks your site every minute. After three failures in a row it emails the workspace owner, and messages a WhatsApp number if you add one. You hear again when the site is back.
A check fails on a connection error, a timeout or any 5xx response. The checks skip your firewall and don't appear in your analytics.
#### Slack and Discord
Under Settings, Alerts, paste an incoming webhook from Slack (Apps, Incoming Webhooks) or Discord (channel settings, Integrations, Webhooks) and choose Connect. Send a test to check it.
Site-down and back-up alerts post there while site-down alerts are on. Production deploys that go live or fail post there too; switch that off with its toggle. Previews never post. The webhook is stored encrypted and never shown again.
#### Log drains
A log drain sends your app's logs to an https:// address, as well as the Logs tab. Set it under Settings, Log drain. Rimbra POSTs a JSON array every few seconds, and can add one header for your service's key. If the address is unreachable, those lines are dropped and the panel shows the error.
```json
[{ "timestamp": "2026-11-07T09:30:00Z", "stream": "stdout", "message": "Server started", "project": "storefront", "deployment": "20261107-093000.000000000" }]
```
#### Deploy hooks
A deploy hook is a private link that starts a deploy. Create one under Settings, Deployments, and use it from a CMS or any tool that can't push to Git. The link is shown once. Anyone who has it can start deploys, so treat it like a password, and replace it if it leaks. An application takes one hook deploy every 30 seconds.
```bash
curl -X POST https://api.rimbra.app/api/hooks/deploy/rdh_your_token
```
#### Preview deployments
Push to any branch other than your production branch and Rimbra builds it at its own link, like my-app-new-cart.rimbra.live. Your live site doesn't change. New pushes to that branch update the same link, and deleting the branch removes the preview.
Previews show in the Deployments tab marked Preview; their menu has Open preview and Promote to production. Turn them off with Preview deploys for pull requests in Settings → General. Private previews keep the links to people in your workspace.
#### No downtime between versions
A new version is started next to the running one. Traffic switches only after it answers, then the old version stops. Visitors never hit a half-built release.
#### Redeploy and cancel
- Redeploy builds the application again with its current source, settings and variables. Use it after changing variables.
- Cancel build stops a queued or building deployment.
- Open any older deployment to read its log and see which commit it built.
#### When a build fails
1. Open the failed deployment and read the last lines of the log first. The error is almost always there.
2. Check the detected framework and entry point at the top of the deployment.
3. If Rimbra picked the wrong folder, set Root directory in Settings → Build & runtime.
4. If a command is wrong, set Install, Build or Start in Settings → Build & runtime.
5. Fix the code or settings, then push again or choose Redeploy.
Common causes: A server that listens on a fixed port instead of PORT, a missing start script, a build that needs an environment variable you have not added yet, a ZIP uploaded one folder too high, or a branch that only has a README (push your code, or pick the branch that has it in Settings).
### Frameworks and runtimes
https://rimbra.app/docs/frameworks
What Rimbra detects, the command it runs for each language, and what your app needs to start.
#### The one rule for servers
If your app is a server, listen on the port in the PORT environment variable. Rimbra picks the port, sets PORT, and routes your domain to it.
```js
// Node.js
const port = process.env.PORT || 3000
app.listen(port)
# Python
port = int(os.environ.get("PORT", 8000))
```
#### What Rimbra detects
| Your files | Detected as | How it runs |
| --- | --- | --- |
| package.json with next | Next.js | Builds, then runs the Next.js server |
| Vite, Astro, SvelteKit, Nuxt config | Frontend framework | Builds and serves the output |
| index.html | Static site | Served as files |
| package.json with a start script or server.js (Express, Fastify, Koa, NestJS, Hono) | Node server | npm start, or node on your entry file |
| requirements.txt or pyproject.toml | Python | Uvicorn for FastAPI, Gunicorn for Flask, runserver for Django |
| composer.json or index.php | PHP | PHP serving public/ or the application root |
| go.mod | Go | go build, then runs the binary |
| Cargo.toml | Rust | cargo build --release, then runs the binary |
| Gemfile with config.ru | Ruby | bundle exec rackup |
| pom.xml or build.gradle | Java | Maven package or Gradle bootJar, then java -jar |
| .csproj, .fsproj or .sln | .NET | dotnet publish, then runs your app. .NET 8 and 10 are available; ASP.NET Core picks up the port by itself |
| Application.cfc, Application.cfm or index.cfm | CFML (Lucee) | Runs on Lucee; box.json dependencies installed with box install |
| Dockerfile | Container | Builds the image and runs it with PORT set |
Dockerfile wins: When a Dockerfile is present, Rimbra builds and runs the container and ignores the other detection rules.
#### Deploy a Docker image
- New application → Docker image runs a ready-made image from Docker Hub, GitHub Container Registry, Quay or any other registry, such as nginx:alpine or ghcr.io/you/app:v1.
- Rimbra pulls the image for its servers (linux/arm64), so the image needs an arm64 or multi-platform build. Build one with docker buildx build --platform linux/amd64,linux/arm64.
- Traffic goes to the port the image exposes, or the PORT it sets. If the app listens somewhere else, enter its port next to the image when you deploy.
- For a private image, tick Private image and add a registry username and a read-only access token. They are saved as RIMBRA_REGISTRY_USERNAME and RIMBRA_REGISTRY_PASSWORD in the application variables and are never passed to the container.
- Redeploy pulls the image again, so a moving tag like latest picks up the newest build. Earlier deployments keep the image they ran, so rollbacks work.
#### Python
- List dependencies in requirements.txt or pyproject.toml.
- Put your app in main.py, app.py, server.py or run.py, or use Django’s manage.py.
- For FastAPI, expose app = FastAPI(). Rimbra runs uvicorn main:app.
- For Flask, expose app = Flask(__name__) and include gunicorn or flask in your dependencies.
#### Node.js
- With a start script, Rimbra runs npm start (or the package manager your lockfile uses).
- Without one, it runs the file in "main", then server.js, index.js or app.js.
- Commit your lockfile so installs are fast and repeatable.
#### Express
An Express app deploys as a Node server with no setup. Add a start script and listen on PORT. The same goes for Fastify, Koa, NestJS and Hono.
- Connect a database to the application and DATABASE_URL is set for you, ready for pg, Prisma or Drizzle.
- Using TypeScript? Add a build script (for example tsc) and start the compiled file: "start": "node dist/server.js".
- An Express API and a frontend in one repo deploy as two applications. Set each one’s Root directory, for example /api and /web.
- Add environment variables on the application’s Variables tab; they are available as process.env on the next deploy.
```js
// server.js
const express = require("express")
const app = express()
app.use(express.json())
app.get("/", (req, res) => res.send("Hello from Rimbra"))
app.listen(process.env.PORT || 3000)
// package.json
{
"scripts": { "start": "node server.js" },
"dependencies": { "express": "^4.19.2" }
}
```
#### React
A React app made with Vite or Create React App builds as a static site: Rimbra runs your build script and serves the output (dist/ or build/).
- Calling your own API? Put its URL in a variable such as VITE_API_URL (or REACT_APP_API_URL for Create React App) and read it with import.meta.env.VITE_API_URL.
- These values are baked into the bundle at build time, so redeploy after changing them.
- React with an Express backend in one repo: create two applications from the same repo, set Root directory to /client for the site and /server for the API, then set the site’s VITE_API_URL to the API’s address.
#### Lucee (CFML)
Applications with Application.cfc, Application.cfm or index.cfm run on Lucee, the open-source CFML engine, with no Dockerfile needed. The Lucee admin is switched off.
- Lucee 6.2 is used by default. Set the runtime version in Settings to 5, 6 or 7 (or an exact line like 6.2) to pick another.
- A box.json with dependencies (ColdBox, modules) is installed with CommandBox’s box install during the build.
- Datasources and secrets go in environment variables, read with server.system.environment.
#### Override the commands
Open Settings → Build & runtime to set your own Install, Build and Start commands and Root directory. Leave a command empty to use the framework’s default. They are used from the next deployment.
| Setting | Default |
| --- | --- |
| Root directory | / |
| Install command | npm ci |
| Build command | npm run build |
| Start command | npm run start |
#### Faster Next.js builds
Rimbra keeps the Next.js build cache between deploys, so a repeat build only redoes what changed. A build script that starts with rm -rf .next would delete that cache, so Rimbra skips that part; every deploy builds in a fresh folder anyway.
Next.js checks every TypeScript file on each build. On a large app that can be half the build time. Turn on Skip TypeScript check in Settings → Build to leave it out.
Type errors then no longer stop a deploy, so keep checking them in your editor or in CI. Turn it off again any time; it applies from the next deploy.
### Environment variables
https://rimbra.app/docs/environment-variables
Give your app API keys, secrets and settings without putting them in your code.
#### Add variables
1. Open the application and go to the Variables tab.
2. Choose Add variable and enter a key and value, such as STRIPE_SECRET_KEY. Pick the environments it applies to, Production, Preview or both, and turn on Secret to hide the value in the dashboard.
3. Or choose Paste .env to add many at once. Comments and blank lines are skipped.
4. Redeploy so the running app picks them up.
#### Read them in your app
```examples
// Node.js
const key = process.env.STRIPE_SECRET_KEY
# Python
import os
key = os.environ["STRIPE_SECRET_KEY"]
// PHP
$key = getenv('STRIPE_SECRET_KEY');
```
#### Set by Rimbra
- PORT: the port your server must listen on.
- Database variables such as DATABASE_URL or REDIS_URL, added when you connect a database.
Build time vs run time: Variables are available during the build and while the app runs. Frameworks that bake values into the browser bundle, like NEXT_PUBLIC_ or VITE_ variables, need a redeploy after you change them.
#### How they are kept safe
- Values are encrypted with AES-256 before they are saved. Our database and its backups only hold the encrypted text.
- The encryption key is kept in a managed key vault, not next to the data, and is only unlocked in memory when our servers start.
- Values are decrypted only to start your app, and are passed to it by name, never on a command line.
- Each app runs in its own sandbox: containers run under gVisor, which gives every app its own kernel, so one app can’t reach another app or our servers.
### Captcha
https://rimbra.app/docs/captcha
Keep bots out of your forms with one click for people and no tracking cookies.
#### Create a site key
1. Open Captcha in the dashboard sidebar and choose New site key.
2. Name it, list the hostnames it runs on (example.com, www.example.com or *.example.com for every subdomain), and pick Checkbox or Invisible.
3. Copy the site key for your page and the secret for your server. The secret is shown only once, so save it right away. If it is lost, roll a new one from the key's page.
Hostnames matter: A key only works on the hostnames you list, so nobody can lift it onto another site. Add localhost while you build, or use the test keys below.
#### Plan allowances
Captcha is on every plan, Free included. A check is one visitor passing the captcha. Checks are counted for your whole workspace each calendar month (UTC), so deleting a site key doesn't take its checks off the count, and test keys never count.
Checks past your plan are paid from your points with pay as you go, at $1 per 10,000, taken in blocks of 1,000. Going a little over doesn't break your forms: with pay as you go off or no points, checks keep working up to twice your plan's checks, then new challenges stop until the month resets, you add points or you upgrade. Keys you made on a higher plan keep working after a downgrade; you just can't add more until you are under the new limit.
| Plan | Site keys | Checks a month |
| --- | --- | --- |
| Free | 1 | 10,000 |
| Launch | 5 | 100,000 |
| Pro | Unlimited | 1,000,000 |
| Scale | Unlimited | 10,000,000 |
#### Add it to your form
Load the script once and put a box where the check should appear. When a visitor passes, the form gets a hidden field called rimbra-captcha-response.
```html
```
#### Options
| Attribute | What it does |
| --- | --- |
| data-sitekey | Your site key. Required. |
| data-mode="invisible" | No box. The check runs when the form is sent, then the form goes through. |
| data-theme | auto (default), light or dark. |
| data-size | normal (default) or compact. |
| data-callback | Name of a function that gets the token when the check passes. |
| data-expired-callback | Called when a token expires after 5 minutes unused. |
| data-error-callback | Called with an error code if the check can’t run. |
#### Difficulty
Each site key has a difficulty, set on its Settings tab. Nothing changes in your code when you switch.
| Difficulty | What visitors do |
| --- | --- |
| Light | One click. The lightest background check, for low-risk forms. |
| Automatic (default) | One click. The background check gets harder for anyone asking often. |
| Strong | One click, then one short puzzle: slide a piece into the gap in a picture. Arrow keys and Enter work too. Two misses bring a new picture. |
Why Strong is harder to fake: The picture is drawn on our servers, so the gap's position never reaches the page, and the drag is checked as well as where it ends. Invisible keys on Strong show the puzzle in the middle of the page when the form is sent.
#### JavaScript API
Boxes render on their own. For pages that build forms later, load the script with ?render=explicit and call render yourself.
```js
const id = rimbraCaptcha.render("#captcha", { sitekey: "YOUR_SITE_KEY", callback: (token) => {} })
rimbraCaptcha.execute(id) // run the check now (invisible keys); returns a promise for the token
rimbraCaptcha.getResponse(id) // the current token, or ""
rimbraCaptcha.reset(id) // start over
rimbraCaptcha.remove(id) // take the box off the page
```
#### Check the token on your server
Before trusting the form, post the token and your secret to siteverify. Form or JSON bodies both work. A token works once and lasts 5 minutes.
```js
const res = await fetch("https://api.rimbra.app/captcha/v1/siteverify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
secret: process.env.RIMBRA_CAPTCHA_SECRET,
response: req.body["rimbra-captcha-response"],
}),
})
const result = await res.json()
// { success: true, challenge_ts: "2026-11-09T10:00:00Z", hostname: "example.com", "error-codes": [] }
if (!result.success) return res.status(400).send("Please confirm you're human.")
```
#### Error codes
| Code | Meaning |
| --- | --- |
| missing-input-secret | No secret was sent. |
| invalid-input-secret | The secret doesn’t belong to any site key. |
| missing-input-response | No token was sent. The visitor may not have done the check. |
| invalid-or-already-seen-response | The token is not valid, or it belongs to another site key. |
| timeout-or-duplicate | The token is older than 5 minutes or was already used. |
| hostname-mismatch | The page’s hostname is no longer on the key. |
#### Test keys
Use these in development and CI. They run on any hostname and never count toward your stats.
| Behaviour | Site key | Secret |
| --- | --- | --- |
| Always passes | rck_test_always_passes | rcs_test_always_passes |
| Always fails | rck_test_always_fails | rcs_test_always_fails |
#### Privacy and how it decides
- No cookies and no tracking. Rimbra keeps no profile of the people who pass.
- The browser solves a small proof of work, usually under a second. Visitors asking unusually often get more work.
- Browsers that say they are automated, and headless browsers, are refused.
- Counts per site key (shown, passed, confirmed, refused) appear in the dashboard for the last 7 days.
### Functions
https://rimbra.app/docs/functions
Small JavaScript or TypeScript handlers that answer HTTP requests at their own address or run before your app.
#### Create a function
1. Open Functions in the dashboard sidebar. Functions is a $4/month add-on on any plan; without it the page asks you to pay first, from your points or by bank transfer, and unlocks as soon as the payment is confirmed. Then choose New function.
2. Give it a name. It answers at https://.rimbra.live right away. Names are shared with apps, so a name belongs to one function or one app.
3. Start from the template in JavaScript or TypeScript, or pick a file from one of your deployed applications, like functions/hello.js.
4. Edit the code in the dashboard and choose Deploy (or press ⌘S). Each deploy is a new version and serves the next request.
```js
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url)
if (url.pathname === "/hello") {
return Response.json({ hello: "world" })
}
return new Response("Not found", { status: 404 })
},
}
```
#### What the code gets
The module exports an object with a fetch method. It runs for every request and returns a Response. Request, Response, Headers, URL, fetch, crypto and the node: built-ins are available. Imports of npm packages are not bundled yet, so keep a function to one file.
| Argument | What it is |
| --- | --- |
| request | The visitor's request: method, url, headers and body, with X-Real-IP set to their address. |
| env | Your variables and secrets as env.NAME, bindings such as env.CACHE, and env.next() for middleware. |
| ctx.waitUntil(promise) | Keeps work going after the response is ready, like logging. The request waits for it to finish. |
State between requests: Each function keeps warm copies of itself, so a module-level variable can survive between requests, but nothing promises it will. Keep real state in a database or a binding.
#### Run before your app
Under Routes, add an application and a path such as /api/* or /login. Every request to that path on the application, at its rimbra.live address and its custom domains, reaches the function first. Return a Response to answer it, or env.next() to let it through to the app. Pass a changed request to env.next() to send different headers on to the app.
```js
export default {
async fetch(request, env) {
if (request.headers.get("authorization") !== `Bearer ${env.ADMIN_TOKEN}`) {
return new Response("Not allowed", { status: 401 })
}
const headers = new Headers(request.headers)
headers.set("x-checked-by", "function")
return env.next(new Request(request, { headers }))
},
}
```
#### Variables, secrets and bindings
- Add variables under Variables and secrets. Secrets are encrypted at rest and never shown again in the dashboard.
- Changes apply to the next request; no redeploy is needed.
- Bindings let the code use other Rimbra products, like await env.CACHE.get("key"). The Bindings card appears once those products are available to your workspace.
#### Logs, metrics and versions
- console.log, warn and error appear under Logs, next to the last 100 requests with their status, time and CPU time.
- Metrics show requests and errors for today and the last 14 days, and p50 and p95 response times.
- The last 10 versions are kept. Roll back to any of them and the next request runs it, with no rebuild.
- Send a test request from the Code card to try the live version without leaving the dashboard.
#### Price and limits
Functions is a $4/month add-on on any plan: 300,000 requests a month and up to 5 functions, then $1 per extra million requests, paid from your points. The $10/month Developer pack includes Functions, Queues, Tunnels and Buckets with double the allowances. No plan includes Functions on its own.
- CPU time counts only the time your code is working, not time spent waiting on fetch or a database. Request and response bodies can be up to 10 MB, and code up to 1 MB.
- Requests reset each billing cycle. Past the included requests they are paid from your points while pay as you go is on. With it off, or with no points left, requests stop at the allowance until the next cycle.
- Without the add-on, functions answer 402 and can be opened, tested and deleted, but not changed or deployed. Nothing is deleted, and adding Functions turns them back on at once.
| | Functions add-on | Developer pack |
| --- | --- | --- |
| Functions | 5 | 10 |
| Requests a month | 300,000 | 600,000 |
| Past that | $1 per million | $1 per million |
| CPU time per request | 50 ms on Free, 200 ms on paid plans | 50 ms on Free, 200 ms on paid plans |
| Memory per function | 128 MB | 128 MB |
| Time to answer | 30 s | 30 s |
#### When a limit is reached
Visitors get a JSON body with an error code and a plain message. The function's logs show the detail.
| Status | error | Meaning |
| --- | --- | --- |
| 402 | plan_required | The workspace doesn't have the Functions add-on, so the function is paused. Adding Functions turns it back on at once. |
| 429 | function_request_limit | The workspace used its included requests and pay as you go is off or out of points. Requests come back when the cycle resets or after adding points. Retry-After is set. |
| 503 | cpu_limit_exceeded | The request used more than its CPU time and was stopped. |
| 503 | memory_limit_exceeded | The function used more than 128 MB and was stopped. |
| 504 | timeout | No answer within 30 seconds. |
| 500 | function_error | The code threw an error or didn't return a Response. |
#### From an AI agent
Through Rimbra MCP, agents can list functions, deploy a function (creating it if needed) and read its logs with list_functions, deploy_function and get_function_logs.
### Load balancing and failover
https://rimbra.app/docs/load-balancing
Run an app in EU Central and EU West at once, move visitors when health checks fail, or split traffic between both.
#### How it works
Load balancing runs a second copy of your app in the other region, so an application in EU Central (Frankfurt) also runs in EU West, and the other way round. Visitors still arrive through the Rimbra edge in Frankfurt, which health-checks both copies and decides where each request goes.
It is part of the Pro and Scale plans and works for apps with a server (Next.js, Node, Python, PHP, Go, Rust, Ruby, Java, .NET, Deno and containers). Static sites are already served from the edge.
Pro and Scale: On Free and Launch the Load balancing tab shows an Upgrade button. If a workspace moves below Pro, traffic goes back to the application's own region and the settings are kept.
#### Turn it on
1. Open your application and go to the Load balancing tab.
2. In Load balancing, turn on Run in both regions.
3. Choose Failover or Spread, and which region is primary.
4. Adjust the health check if your app has a health route, for example /health.
#### Failover and Spread
- Failover: every visitor goes to the primary region. When its copy fails the health check the set number of times in a row, traffic moves to the other region. It moves back once the primary passes the healthy threshold again.
- Spread: new visitors are split between both copies, 50/50, 70/30 or 90/10. If a copy fails its checks, everyone goes to the healthy one until it recovers.
- Keep visitors on one copy (Spread): a cookie remembers the region a visitor started on, so sessions kept in memory survive. A visitor only moves when their copy fails.
- Each response carries an X-Rimbra-Region header (eu-central or eu-west) so you can see which copy answered.
#### Health checks
Thresholds count checks in a row, so a single slow answer never moves traffic. A copy that has never been checked never gets traffic in its place, and the primary keeps its traffic until checks say it is down, so turning load balancing on never takes a working site offline.
The Load balancing tab shows each copy's health, response time and when it was last checked, plus a history of every switch.
| Setting | Default | Range |
| --- | --- | --- |
| Path | / | Any path on your app |
| Every | 10 seconds | 5 to 300 seconds |
| Timeout | 3 seconds | 1 to 30 seconds, shorter than the interval |
| Healthy after | 2 checks in a row | 1 to 10 |
| Down after | 3 checks in a row | 1 to 10 |
| Expected status | Any 2xx or 3xx | One exact status, like 200 |
#### Alerts
When traffic moves, and when it moves back, the application owner gets an email. If the application has Slack or Discord alerts connected, the same message is posted there.
#### Data, sessions and limits
- Databases stay in EU Central. The copy in EU West reaches them over Rimbra's private link, which adds a few milliseconds to each query.
- Environment variables and secrets are the same for both copies.
- The second copy is built from the same commit after every production deploy. Until it is ready it takes no traffic, and the card shows it as not running yet.
- Second copies need an application connected to GitHub, GitLab or Bitbucket. Docker apps and uploaded folders can't have one yet.
- If a page request can't reach the copy it was sent to, it is tried once on the other healthy copy. Only GET and HEAD requests are retried.
- Files your app writes to its own disk (uploads, SQLite, caches) are not shared between copies. Keep them in a database or object storage.
- Sessions kept in memory are not shared either. Store sessions in a database or Redis, or turn on Keep visitors on one copy.
- Background work that must run once (cron jobs, queue workers started by the app itself) runs in both copies. Use Rimbra cron jobs for anything that must run exactly once.
### Tunnels
https://rimbra.app/docs/tunnels
Put a server on your computer at a public HTTPS address with rimbra tunnel, for webhooks, demos and testing on your phone.
#### Install the CLI
On macOS and Linux, run the installer. It puts rimbra in /usr/local/bin, or ~/.local/bin when that isn’t writable. On Windows, download rimbra-windows-amd64.exe from the link below and run it from a terminal.
```bash
curl -fsSL https://rimbra.app/install.sh | sh
rimbra version
```
#### Sign in with a tunnel token
1. Open Tunnels in the dashboard sidebar and choose New token. Give it the name of the computer it’s for.
2. Copy the token. It’s shown only once; Rimbra keeps only a hash of it.
3. Run rimbra login and paste it. It’s saved in ~/.config/rimbra/config.json, readable only by you.
In CI or scripts: Set RIMBRA_TOKEN instead of running rimbra login, or pass --token for a single run.
#### Start a tunnel
Give the port your app listens on. The address prints at once and each request is listed as it arrives. Press Ctrl+C to stop.
```bash
rimbra tunnel 3000
https://tunnel-8f2c1a9e.rimbra.live → http://localhost:3000
10:42:07 GET / 200 14ms
10:42:08 POST /api/hook 204 3ms
```
#### Options
| Flag | What it does |
| --- | --- |
| --name api | A fixed address, api-.rimbra.live, the same every run. |
| --auth user:pass | Visitors sign in with this username and password before anything reaches your machine. |
| --host 127.0.0.1 | The local host to forward to. Defaults to localhost. |
| --host-header preserve | Pass the public address as Host. By default your app sees Host: localhost:. |
| --token rtk_… | Use this token for one run instead of the saved one. |
Reconnects: If the connection drops, the CLI reconnects on its own and keeps the same address for up to an hour.
#### What your app receives
- Every method, header and body, with request bodies up to 32 MB.
- Streamed responses and server-sent events, as your app writes them.
- Websockets, including the hot reload of most dev servers.
- X-Forwarded-For with the visitor’s IP, X-Forwarded-Host with the public address and X-Forwarded-Proto: https.
- Your app has 60 seconds to start answering. After that the visitor gets a 504.
#### Price and limits
Tunnels is a $4/month add-on on any plan: 1 tunnel with its own subdomain and 5 GB transfer a month, then $0.20 per GB from your points. The $10/month Developer pack includes Functions, Queues, Tunnels and Buckets with double the allowances. Without the add-on the Tunnels page asks you to pay first, and new tokens can’t be made.
Transfer counts what goes through a tunnel in both directions and resets each billing cycle. Past it, pay as you go pays from your points; with it off, or with no points left, tunnels stop carrying requests until the next cycle.
Without the add-on the CLI is refused with 402 plan_required until you add Tunnels. Tokens are kept, so the same ones work again.
A tunnel with no requests for 4 hours closes on its own. A tunnel never takes the address of a live app: if a name is already an app’s, pick another.
| | Tunnels at once | Transfer a month | Past that |
| --- | --- | --- | --- |
| Tunnels add-on, $4/month | 1, with a fixed name | 5 GB | $0.20 per GB |
| Developer pack, $10/month | 2, with fixed names | 10 GB | $0.20 per GB |
#### See and stop tunnels
The Tunnels page charts requests through your tunnels by status over the last 24 hours or 7 days (30 days on paid plans), lists every connected tunnel with its address, local port, start time and request count, and shows the latest 50 requests across them. Open a tunnel to see only its requests. Stop ends a tunnel and tells the CLI not to reconnect. Tokens live on their own tab; revoking a token closes every tunnel using it.
#### Messages from the CLI
| Message | What to do |
| --- | --- |
| That tunnel token isn’t valid | The token was revoked or mistyped. Make a new one and run rimbra login. |
| Tunnels need the Tunnels add-on | Add Tunnels from its page in the dashboard, then run the command again. |
| N tunnels are already running | Stop one in the dashboard, or get the Developer pack for two at once. |
| An app already lives at … | Pick a different --name. |
| Nothing is running on port N (in the browser) | The tunnel is up but your app isn’t. Start it and refresh. |
### Create a database
https://rimbra.app/docs/databases
Managed PostgreSQL, MySQL and Redis that run next to your apps.
#### Engines
| Engine | Version | Good for |
| --- | --- | --- |
| PostgreSQL | 16 | Most apps. Works with Prisma, Drizzle, Django, Rails and Laravel |
| MySQL | 8.4 | Laravel, WordPress and other PHP apps |
| Redis | 7 | Caching, sessions, queues and rate limits |
#### Create one
1. Open Hosting → Databases and choose New database, or choose New → Database from any page.
2. Pick PostgreSQL, MySQL or Redis and give it a name.
3. Choose Create. It is ready in a few seconds.
4. Open it to see its connection details, data and backups.
#### Connect from outside Rimbra
Databases are private by default: only applications deployed on Rimbra can reach them. To use a Postgres database from your laptop, a database tool or an app hosted elsewhere, open it and turn on Outside connections. It restarts for a few seconds and gets its own address at db.rimbra.app.
Outside connections are encrypted only (TLS 1.2 or newer). Unencrypted connections are refused. The certificate is a public Let’s Encrypt one for db.rimbra.app, so clients can verify it.
```bash
psql "postgres://rimbra:PASSWORD@db.rimbra.app:5400/mydb?sslmode=require"
# full certificate checks (libpq 16 or newer)
psql "postgres://rimbra:PASSWORD@db.rimbra.app:5400/mydb?sslmode=verify-full&sslrootcert=system"
```
MySQL and Redis: Outside connections are available for Postgres today. MySQL and Redis are next.
#### Inside a database
| Tab | Use it to |
| --- | --- |
| Overview | Storage, connections and memory, the connection details, Outside connections, and the applications that use it |
| Tables (Keys for Redis) | Browse tables and rows, or keys for Redis, and edit them |
| Query (Console for Redis) | Run SQL, or Redis commands |
| Backups | Back up now and restore a backup |
| Variables | Copy the connection variables |
| Logs | Read the database server log |
| Settings | Restart or delete the database |
Keep the URL private: A connection URL contains the password. Keep it in environment variables, never in code, screenshots or public repositories.
### Connect a database to an application
https://rimbra.app/docs/connections
Attach a database and your app reads it from an environment variable.
#### Connect
1. Open the database under Hosting → Databases.
2. On its Overview, under Used by, pick the application. Injected as defaults to DATABASE_URL (REDIS_URL for Redis); change it if your app expects another name.
3. Choose Connect and redeploy. The app gets the variables on that deploy.
#### Variables you get
| Engine | Variables |
| --- | --- |
| PostgreSQL | DATABASE_URL, PGHOST, PGPORT, PGUSER, PGPASSWORD, PGDATABASE |
| MySQL | DATABASE_URL, MYSQL_URL, MYSQL_HOST, MYSQL_PORT, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE |
| Redis | REDIS_URL, REDIS_HOST, REDIS_PORT, REDIS_PASSWORD |
#### Use it in your app
```examples
// Prisma (schema.prisma)
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// node-postgres
import pg from "pg"
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL })
# Python
import os, psycopg
conn = psycopg.connect(os.environ["DATABASE_URL"])
```
#### Disconnect
Disconnecting removes the variable from the application on its next deploy. The database and its data stay as they are.
### Data, queries and backups
https://rimbra.app/docs/backups
Look at your data, run queries and keep restore points without another tool.
#### Browse and edit data
The Tables tab lists your tables. Open one to page through rows, add a row, edit values or delete rows. For Redis, the Keys tab lists keys with their type and value.
#### Run queries
Query runs one SQL statement at a time against PostgreSQL or MySQL and shows the result as a table. Writes work too, so be careful on production data. For Redis, the Console tab runs commands such as GET, SET and KEYS. Commands that could wipe or reconfigure the server are blocked.
#### Back up and restore
1. Open Backups and choose Back up now. PostgreSQL and MySQL take a full dump; Redis saves a snapshot.
2. To restore, choose Restore on a backup and confirm.
3. Rimbra takes a safety backup of the current data first, so a restore can be undone.
Before big changes: Take a backup before running migrations or bulk edits from the Query tab.
#### Daily backups
Rimbra backs up every database each night, after 01:00 UTC. Daily backups appear in the same list as yours, marked Daily backup, and you can download or restore them the same way. Free keeps the last 3, Launch 7, Pro 14 and Scale 30. Older daily backups are removed on their own; backups you take yourself stay until you delete them. Backups count toward your plan storage.
#### Import from Supabase, Railway or another server
Rimbra copies a PostgreSQL or MySQL database from anywhere it can reach on the internet. Your source is only read, never changed, and its password is used for the copy and not stored.
1. Open Hosting → Databases and choose Import database. To fill a database you already have, open its Backups tab and choose Import data instead.
2. Supabase: click Connect in your Supabase project, pick Session pooler and copy the URI with your password filled in. The direct connection only works over IPv6, which Rimbra can’t reach.
3. Railway: open the database’s Variables and copy DATABASE_PUBLIC_URL (MYSQL_PUBLIC_URL for MySQL). Turn on Public Networking first if it isn’t there.
4. Paste the connection string and start the import. The database page shows progress, then how many tables came across.
5. Point your app’s DATABASE_URL at the new database to switch over.
What stays behind: From Supabase, your tables, data, functions, row-level security policies and auth users come across. Supabase’s own services (Realtime, Storage files, Vault, GraphQL) and extensions Rimbra doesn’t run are skipped, and the import lists anything it skipped.
#### Use your own tools
Your apps on Rimbra reach the database directly through the connected variables. To use a desktop client like TablePlus or DBeaver with PostgreSQL, turn on Outside connections (see Create a database). For MySQL and Redis, use the Tables and Query tabs for now.
### KV storage
https://rimbra.app/docs/kv
Key-value namespaces for config, sessions, feature flags and cached answers, read and written over HTTPS.
#### Create a namespace and a token
1. Open KV in the dashboard sidebar and choose New namespace. Give it a name; apps use its id (kvns_…).
2. Under KV tokens choose New token. Copy it right away: it is shown only once, and Rimbra keeps only a hash and the last few characters.
3. Add the token to your application as a secret variable, for example RIMBRA_KV_TOKEN, and the namespace id as RIMBRA_KV_NAMESPACE.
One token, every namespace: A KV token can read and write every namespace in the workspace. Make one per app so you can delete it on its own if it leaks.
#### Read and write
Send the token as Authorization: Bearer. Values are raw bytes in the request and response body, so text, JSON and binary all work. Keys go in the path, URL-encoded, so a key like user/42 becomes user%2F42.
```bash
KV=https://api.rimbra.app/api/kv/v1/namespaces/$RIMBRA_KV_NAMESPACE
# Write a value that expires in an hour
curl -X PUT "$KV/values/session:abc?ttl=3600" \
-H "Authorization: Bearer $RIMBRA_KV_TOKEN" \
--data-binary '{"user":42}'
# Read it (404 when missing or expired)
curl "$KV/values/session:abc" -H "Authorization: Bearer $RIMBRA_KV_TOKEN"
# Delete it
curl -X DELETE "$KV/values/session:abc" -H "Authorization: Bearer $RIMBRA_KV_TOKEN"
```
#### From Node
```js
const KV = `https://api.rimbra.app/api/kv/v1/namespaces/${process.env.RIMBRA_KV_NAMESPACE}`;
const auth = { Authorization: `Bearer ${process.env.RIMBRA_KV_TOKEN}` };
export async function kvGet(key) {
const res = await fetch(`${KV}/values/${encodeURIComponent(key)}`, { headers: auth });
return res.ok ? res.text() : null;
}
export async function kvSet(key, value, ttlSeconds) {
const q = ttlSeconds ? `?ttl=${ttlSeconds}` : "";
await fetch(`${KV}/values/${encodeURIComponent(key)}${q}`, { method: "PUT", headers: auth, body: value });
}
```
#### Endpoints
All paths start with /api/kv/v1/namespaces/{namespace_id}.
| Request | What it does |
| --- | --- |
| GET /values/{key} | The value as raw bytes. Metadata comes back in the X-Kv-Metadata header and the expiry (unix seconds) in X-Kv-Expiration. |
| PUT /values/{key} | Write the request body as the value. Optional ?ttl=seconds or ?expiration=unix-time, and X-Kv-Metadata with up to 1 KB of JSON. |
| DELETE /values/{key} | Remove the key. Deleting a key that does not exist is not an error. |
| GET /keys?prefix=&cursor=&limit= | Keys in order with size, expiry and metadata, up to 1,000 a page. When complete is false, pass the returned cursor for the next page. |
| POST /bulk | Up to 10,000 writes and deletes at once: {"put":[{"key","value","base64","ttl","expiration","metadata"}],"delete":["key"]}. Writes are all-or-nothing. |
| GET /api/kv/v1/namespaces | Your namespaces with their key counts and size. |
#### Limits
- Keys: up to 512 bytes of UTF-8. Values: up to 25 MB. Metadata: up to 1 KB of JSON.
- Expiry: at least 60 seconds away. Expired keys read as missing at once and are cleared within minutes.
- Reads and writes are counted per UTC day; a list counts as a read, and each item in a bulk request as one write.
- What you store in KV counts toward your plan’s storage, the same pool as application files and databases.
| Plan | Namespaces | Reads a day | Writes a day |
| --- | --- | --- | --- |
| Free | 1 | 100,000 | 1,000 |
| Launch | 10 | 1,000,000 | 1,000,000 |
| Pro | 50 | 10,000,000 | 10,000,000 |
| Scale | 200 | 100,000,000 | 100,000,000 |
#### How it stores your data
KV lives in Rimbra’s own database in the region, so a read right after a write always sees it, and a bulk write lands all at once or not at all. It is backed up with the rest of your workspace.
Your apps on Rimbra reach the API over the same network, so reads are fast. For values your app reads on every request, keep a short in-memory cache in front of it.
### Buckets
https://rimbra.app/docs/buckets
S3-compatible storage for user uploads, media, exports and backups. Works with django-storages, boto3, the AWS CLI and every S3 SDK.
#### When to use a bucket
An app’s own folder is rebuilt on every deploy, so files your users upload there are lost the next time you deploy. Put them in a bucket instead: it keeps files apart from your code, serves them over HTTPS, and works with the S3 libraries your framework already has.
Buckets is a $3/month add-on on any plan: 5 GB stored, 500,000 Class A writes and 5 million Class B reads a month, then $0.03 per GB-month, $6 per million writes and $0.50 per million reads from your points. The $10/month Developer pack includes Functions, Queues, Tunnels and Buckets with double the allowances. Downloads are always free. See Pricing below.
#### Create a bucket and a key
1. Open Buckets in the dashboard sidebar. Without the add-on the page asks you to pay first and unlocks as soon as the payment is confirmed. Then choose New bucket. Names are 3 to 63 lowercase letters, numbers and dashes, and are shared by everyone on Rimbra, so shop-media-prod is a better pick than media.
2. Leave it Private unless every file in it is meant for anyone to see.
3. Under Access keys choose New key. Pick Read and write or Read only, and all buckets or just the ones this app needs.
4. Copy the variables it shows into your application’s Variables tab. The secret is shown only once; if you lose it, make a new key and revoke the old one.
```bash
AWS_ACCESS_KEY_ID=RK…
AWS_SECRET_ACCESS_KEY=…
AWS_ENDPOINT_URL_S3=https://s3.rimbra.app
AWS_REGION=auto
```
Every client needs the same three settings: Endpoint https://s3.rimbra.app, region auto (us-east-1 also works), and path-style addresses (https://s3.rimbra.app//). Signature version 4 only.
#### Django
Install django-storages and boto3 (pip install django-storages boto3), then point Django’s default storage at your bucket. FileField and ImageField uploads, default_storage and file.url all use it from then on.
```python
# settings.py
import os
STORAGES = {
"default": {"BACKEND": "storages.backends.s3.S3Storage"},
"staticfiles": {"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"},
}
AWS_ACCESS_KEY_ID = os.environ["AWS_ACCESS_KEY_ID"]
AWS_SECRET_ACCESS_KEY = os.environ["AWS_SECRET_ACCESS_KEY"]
AWS_STORAGE_BUCKET_NAME = "shop-media"
AWS_S3_ENDPOINT_URL = "https://s3.rimbra.app"
AWS_S3_REGION_NAME = "auto"
AWS_S3_ADDRESSING_STYLE = "path"
AWS_S3_SIGNATURE_VERSION = "s3v4"
AWS_DEFAULT_ACL = None
AWS_S3_FILE_OVERWRITE = False
# Private bucket: file.url is a signed link that lasts an hour.
AWS_QUERYSTRING_AUTH = True
# Public bucket instead: plain links with no signature.
# AWS_QUERYSTRING_AUTH = False
# AWS_S3_CUSTOM_DOMAIN = "s3.rimbra.app/shop-media"
```
#### Django notes
- Moving from local MEDIA_ROOT: upload the existing folder once with aws s3 sync ./media s3://shop-media/ (see the AWS CLI below). Saved paths in your database stay the same.
- Static files can stay on your app (whitenoise or Django’s static serving). Only user uploads need the bucket.
- AWS_S3_FILE_OVERWRITE = False keeps two uploads with the same name apart, as Django does on disk.
- If your settings set AWS_DEFAULT_ACL = "public-read", it’s accepted and ignored: a bucket is public or private as a whole, set in its settings.
#### Node
```js
// npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const s3 = new S3Client({
endpoint: "https://s3.rimbra.app",
region: "auto",
forcePathStyle: true,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
});
await s3.send(new PutObjectCommand({ Bucket: "shop-media", Key: "avatars/42.png", Body: buffer, ContentType: "image/png" }));
// A link to a private file that works for an hour
const url = await getSignedUrl(s3, new GetObjectCommand({ Bucket: "shop-media", Key: "avatars/42.png" }), { expiresIn: 3600 });
```
#### Python (boto3)
```python
import os
import boto3
from botocore.config import Config
s3 = boto3.client(
"s3",
endpoint_url="https://s3.rimbra.app",
aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"],
aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"],
region_name="auto",
config=Config(signature_version="s3v4", s3={"addressing_style": "path"}),
)
s3.upload_file("report.pdf", "shop-media", "reports/2026-11.pdf") # multipart above 8 MB
for obj in s3.list_objects_v2(Bucket="shop-media", Prefix="reports/").get("Contents", []):
print(obj["Key"], obj["Size"])
url = s3.generate_presigned_url("get_object", Params={"Bucket": "shop-media", "Key": "reports/2026-11.pdf"}, ExpiresIn=3600)
```
#### AWS CLI
Tell the CLI once to use path-style addresses, then pass the endpoint and region with each command (or set AWS_ENDPOINT_URL_S3 and AWS_REGION).
```bash
aws configure set default.s3.addressing_style path
aws s3 ls s3://shop-media --endpoint-url https://s3.rimbra.app --region auto
aws s3 cp ./photo.jpg s3://shop-media/photos/ --endpoint-url https://s3.rimbra.app --region auto
aws s3 sync ./media s3://shop-media/media --endpoint-url https://s3.rimbra.app --region auto
aws s3 presign s3://shop-media/photos/photo.jpg --expires-in 3600 --endpoint-url https://s3.rimbra.app --region auto
```
#### Public and private buckets
- Private (the default): every request needs an access key, or a signed link that expires. Use it for user uploads, invoices, exports and backups.
- Public: anyone can read a file at https://s3.rimbra.app//, for example product images on a shop. Listing the bucket and writing to it still need a key.
- Signed links last from a minute up to 7 days. Make them from your code (presigned GET, as above) or from a file’s panel in the dashboard.
- Files are served with their content type, ETag, Last-Modified and Cache-Control, and support Range requests. They are sandboxed (Content-Security-Policy: sandbox), so an uploaded HTML or SVG file can’t run scripts. Buckets are for files, not for hosting sites; deploy sites as applications.
#### Uploads straight from the browser
To let users upload without the file passing through your server, have your server make a presigned PUT URL and send it to the page. The browser then PUTs the file to it directly.
Add your site’s origin under the bucket’s Settings → CORS first (for example https://shop.example.com), or the browser blocks the request.
```js
// On your server
import { PutObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const key = `uploads/${crypto.randomUUID()}-${fileName}`;
const uploadUrl = await getSignedUrl(s3, new PutObjectCommand({ Bucket: "shop-media", Key: key, ContentType: fileType }), { expiresIn: 600 });
// In the browser
await fetch(uploadUrl, { method: "PUT", body: file, headers: { "Content-Type": file.type } });
```
#### In the dashboard
- Open a bucket to browse it by folder. Drop files or whole folders anywhere on the list to upload them; large files go up in parts, with progress for each.
- Select files or folders to delete them together. Rename or move a file or a folder from its row or its panel.
- Click a file to preview images, PDFs and text, copy its public link, make a signed link (1 hour, 1 day or 7 days), or download it.
- Folders are just the part of a key before a slash. New folder adds an empty marker (folder/) so it shows before anything is in it.
- Activity on the Buckets page, and Metrics on each bucket, chart bytes stored, writes and lists (Class A), reads (Class B) and bytes downloaded over 24 hours or 7 days, and 30 days on paid plans. They count the same requests billing does, so the dashboard's own uploads and previews aren't in them.
- Each bucket, tab and folder has its own address (for example app.rimbra.app/buckets/shop-media/files/avatars), so Back and Forward work and you can share a link to a folder.
#### Limits
- Files can be up to 5 GB, in one PUT or a multipart upload. Clients switch to multipart for large files on their own; parts are 5 MB to 5 GB, up to 10,000 parts.
- Keys (file names) can be up to 1,024 bytes of UTF-8. Listings are in byte order, 1,000 per page.
- Unfinished multipart uploads are removed after 7 days.
- Up to 50 access keys per workspace, and up to 20 CORS origins per bucket.
- Without the Buckets add-on (or the Developer pack), buckets and access keys can’t be created; the API answers 402. If the add-on ends, reads and writes stop until you add Buckets again. Nothing is deleted.
#### Pricing
The Buckets add-on ($3/month) and the Developer pack ($10/month) include storage and operations each month, on any plan. No plan includes Buckets on its own. Past the allowance, you pay only for what you use, from your Rimbra points. Downloads are free, with no egress charge and nothing taken from your plan’s bandwidth.
- Storage is measured every hour and added up over the month. A GB-month is 1 GB kept for a whole month, so 20 GB kept for half a month is 10 GB-months.
- Class A: PutObject, CopyObject, CreateMultipartUpload, UploadPart, CompleteMultipartUpload, ListObjects, ListBuckets, ListParts, ListMultipartUploads, CreateBucket and other writes. Class B: GetObject, HeadObject, HeadBucket and other reads. Deletes are free. Requests you make in the dashboard aren’t counted.
- The allowance resets at the start of each billing cycle, the day your workspace signed up. Charges are worked out every hour and taken from your balance once a day; they show in Billing next to your top-ups.
- Top up from the Buckets page or Billing ($5, $10, $25, $50 or any amount). The balance never expires and also pays for plans, add-ons and domains.
- We email you at 80% of an allowance, when your balance drops under $2 while you’re past the allowance, and if it runs out.
- Past the allowance with pay as you go off, or a balance of zero or less, uploads and other writes are refused with AccountProblem until you top up or turn it on. Reads and deletes keep working; with an empty balance, for 30 days.
| | Buckets add-on | Developer pack | Past the allowance |
| --- | --- | --- | --- |
| Storage | 5 GB-months | 10 GB-months | $0.03 per GB-month |
| Class A operations (writes and lists) | 500,000 | 1 million | $6.00 per million |
| Class B operations (reads) | 5 million | 10 million | $0.50 per million |
| Downloads | Free | Free | Free |
#### Worked example
A Django shop with the Buckets add-on keeps 26 GB of product photos and customer uploads all month. Its app makes 1.5 million uploads and listings and serves 18 million reads, and visitors download 400 GB.
| | Used | Included | Billed | Cost |
| --- | --- | --- | --- | --- |
| Storage | 26 GB-months | 5 GB-months | 21 GB-months × $0.03 | $0.63 |
| Class A | 1.5 million | 500,000 | 1 million × $6.00 | $6.00 |
| Class B | 18 million | 5 million | 13 million × $0.50 | $6.50 |
| Downloads | 400 GB | — | Free | $0.00 |
| Total from the balance | | | | $13.13 |
Within the allowance, buckets cost nothing extra: A site that keeps 4 GB and serves a few million reads a month stays inside the add-on’s allowance and pays only the $3 a month.
#### Supported S3 operations
ListBuckets, CreateBucket, HeadBucket, DeleteBucket (when empty), GetBucketLocation, Get/Put/DeleteBucketCors, ListObjectsV2, ListObjects, GetObject (Range, If-None-Match, If-Modified-Since, response-content-* overrides on signed requests), HeadObject, PutObject (Content-MD5 and x-amz-checksum-* are checked), CopyObject, DeleteObject, DeleteObjects, and multipart: CreateMultipartUpload, UploadPart, UploadPartCopy, CompleteMultipartUpload, AbortMultipartUpload, ListParts, ListMultipartUploads.
ACL and tagging requests are accepted and change nothing. Versioning, lifecycle rules, bucket policies and virtual-hosted addresses (bucket.s3.rimbra.app) are not supported.
### Queues
https://rimbra.app/docs/queues
Send messages to a named queue and have them delivered to your code, with retries, signatures and failed-message replay.
#### Create a queue
1. Open Queues in the dashboard sidebar. Queues is a $3/month add-on on any plan; without it the page asks you to pay first and unlocks as soon as the payment is confirmed. Then choose New queue.
2. Give it a name. Producers send to this name, so it uses lowercase letters, numbers and dashes and doesn't change later.
3. Choose Push to a consumer and pick one of your apps plus a path, or any HTTPS address. Or choose Consumers pull if your workers will fetch messages themselves.
4. Copy the signing secret. It is shown only once; roll a new one from the queue if it is lost.
5. Under API tokens, create a token for the servers that send messages. It is also shown only once.
Your own apps: Pick the app's .rimbra.live, .xyz or .click address. Deliveries leave Rimbra over HTTPS and never reach private or internal addresses.
#### Send messages
POST to the queue with your token. The body is JSON, or text when you set contentType to text, up to 128 KB. Send up to 100 at once with a messages array.
```bash
curl -X POST https://api.rimbra.app/api/queues/v1/emails/messages \
-H "Authorization: Bearer $RIMBRA_QUEUES_TOKEN" \
-H "Content-Type: application/json" \
-d '{"body": {"to": "ada@example.com"}, "delaySeconds": 60, "dedupeId": "welcome-42"}'
# 202 {"id": "m_…"}
# Batch: {"messages": [{"body": …}, {"body": "text", "contentType": "text"}]}
# 202 {"messages": [{"id": "m_…"}, {"id": "m_…", "duplicate": true}]}
```
#### Message fields
| Field | What it does |
| --- | --- |
| body | The message. Any JSON value, or a string with contentType text. Required. |
| contentType | json (default) or text. |
| delaySeconds | Hold the message back for up to 43200 seconds (12 hours). |
| dedupeId | Up to 128 characters. Sending the same id again within 24 hours answers the first message's id and adds nothing. |
#### Receive deliveries
Push queues post batches to your consumer as JSON: {"queue", "deliveryId", "messages": [{"id", "attempts", "createdAt", "contentType", "body"}]}. Answer with any 2xx within 30 seconds and the whole batch is done.
To finish only part of a batch, answer 2xx with {"retry": ["m_…"]}; those messages are tried again. Any other answer, or no answer, retries the whole batch.
#### Check the signature
Every delivery carries Rimbra-Signature: t=,v1=. v1 is HMAC-SHA256 of "t.body" with the queue's signing secret. Compare it in constant time and refuse timestamps more than 5 minutes old.
```js
import crypto from "node:crypto"
export async function POST(req) {
const raw = await req.text()
const parts = Object.fromEntries((req.headers.get("rimbra-signature") ?? "").split(",").map((p) => p.split("=")))
const expected = crypto.createHmac("sha256", process.env.RIMBRA_QUEUE_SECRET).update(`${parts.t}.${raw}`).digest("hex")
const a = Buffer.from(expected), b = Buffer.from(parts.v1 ?? "")
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300 || a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return new Response("bad signature", { status: 401 })
}
for (const m of JSON.parse(raw).messages) await handle(m.body)
return new Response(null, { status: 204 })
}
```
#### Pull instead of push
Pull queues wait for your workers. Lease up to 100 messages, handle them, then ack by lease id. A lease that runs out puts the message back with one attempt counted; nack puts it back at once or after a delay.
```bash
curl -X POST https://api.rimbra.app/api/queues/v1/jobs/lease -H "Authorization: Bearer $RIMBRA_QUEUES_TOKEN" \
-d '{"max": 10, "visibilitySeconds": 60}'
# {"messages": [{"id": "m_…", "leaseId": "l_…", "attempts": 1, "body": …}]}
curl -X POST https://api.rimbra.app/api/queues/v1/jobs/ack -H "Authorization: Bearer $RIMBRA_QUEUES_TOKEN" -d '{"leaseIds": ["l_…"]}'
curl -X POST https://api.rimbra.app/api/queues/v1/jobs/nack -H "Authorization: Bearer $RIMBRA_QUEUES_TOKEN" -d '{"leaseIds": ["l_…"], "delaySeconds": 30}'
```
#### Retries and failed messages
- Each queue has attempts (1 to 20, default 5) and a first retry wait (default 10 seconds). The wait doubles each attempt, up to an hour, with a little randomness so retries don't arrive together.
- After the last attempt, a message moves to the queue's failed messages with its last error. Replay one or all of them once your consumer is fixed; replay starts attempts from zero.
- Pause a push queue to hold deliveries while you fix something. Messages keep arriving and wait.
#### What Rimbra guarantees
- At least once: a message is removed only after your consumer answers 2xx or acks it. If an answer is lost, the message is sent again, so make handlers safe to repeat. The message id stays the same across retries.
- Durable: messages are written to disk before the send call answers, and survive restarts. A delivery cut short by a restart is sent again.
- Order is roughly oldest first, not strict.
- Messages not delivered within 3 days are removed.
#### Price and limits
Queues is a $3/month add-on on any plan: 500,000 messages a month, up to 3 queues, kept 3 days, then $1 per extra million messages from your points. The $10/month Developer pack includes Functions, Queues, Tunnels and Buckets with double the allowances. No plan includes Queues on its own.
Messages reset each billing cycle. Past the included messages, pay as you go pays from your points; with it off, or with no points left, sends answer 429 until the next cycle. Messages waiting in your queues count toward your plan's storage.
If the add-on ends, sends answer 402 and queues can't be changed or made until you add Queues again. Messages already waiting are still delivered and can be pulled, replayed or purged, and nothing is deleted.
| | Queues | Messages a month | Kept for | Past that |
| --- | --- | --- | --- | --- |
| Queues add-on, $3/month | 3 | 500,000 | 3 days | $1 per million |
| Developer pack, $10/month | 6 | 1 million | 3 days | $1 per million |
### Buy a domain
https://rimbra.app/docs/domain-search
Search, buy and point a domain at your application, with DNS and HTTPS set up for you.
#### Find a name
Names that are already registered show Taken. Premium names and names that can’t be registered here show Unavailable.
1. Open Hosting → Domains and choose Buy domain.
2. Type a name, with or without an ending, like yourcompany or yourcompany.com.
3. Results show whether each name is available and its price per year, with the renewal price.
#### Buy it
1. Choose Buy on an available name.
2. Enter the owner’s contact details. Registries require them, and they are attached to the domain.
3. Choose add-ons such as WHOIS privacy, which hides those details from public lookups.
4. Review the total and confirm. The domain is registered right away.
#### After you buy
- Rimbra manages the domain’s DNS. It points at Rimbra within a few minutes, with nothing for you to set.
- Connect it to an application from the domain’s page or the application’s Domains tab. HTTPS is issued automatically.
- The domain page shows the renewal date.
- You can also add it as an email sending domain in Emails → Domains.
### Connect a domain you already own
https://rimbra.app/docs/project-domains
Point a domain from Namecheap, GoDaddy, Cloudflare or any registrar at a Rimbra application, and pick its primary domain.
#### Add it to the application
1. Open the application and go to the Domains tab. You can also start from Hosting → Domains.
2. Choose Add domain, enter the hostname, like yourcompany.com or app.yourcompany.com, and choose Add domain.
3. The record to add shows under the domain, with copy buttons.
#### Add the record at your registrar
Remove any other A, AAAA or CNAME records on the same name, or visitors may land somewhere else.
| Domain | Type | Name | Value |
| --- | --- | --- | --- |
| yourcompany.com (root) | A | @ | The IP address shown on the domain page |
| app.yourcompany.com (subdomain) | CNAME | app | cname.rimbra.app |
| *.yourcompany.com (wildcard) | CNAME | * | cname.rimbra.app |
#### Wait for it to go live
The domain page checks DNS every 20 seconds. Once the record is found, Rimbra issues a free SSL certificate and the site answers on your domain. This usually takes a few minutes, sometimes up to an hour while DNS spreads.
Cloudflare: Set the record to DNS only (grey cloud). With the proxy on, Rimbra can’t see the record or issue the certificate.
#### Wildcard domains
Connect *.yourcompany.com to send every subdomain one level down, like acme.yourcompany.com and globex.yourcompany.com, to one application. Useful when each customer gets their own address. Your app reads the Host header to tell them apart.
A wildcard only starts serving once you prove you own the domain: the domain page shows a TXT record named _rimbra-verify to add. Then add a CNAME named * pointing at cname.rimbra.app.
A name you connect on its own always wins, so shop.yourcompany.com can serve a different application. A wildcard covers one level only: a.b.yourcompany.com needs *.b.yourcompany.com. The domain itself (yourcompany.com) is connected separately.
Each subdomain gets its own SSL certificate on its first secure visit, which takes a few seconds. To stay inside the certificate authority’s weekly limit, up to 25 new subdomains a week get a certificate through a wildcard; subdomains that already have one keep renewing.
#### Move it between applications
Open the domain under Hosting → Domains, pick another application under Application and choose Move domain. It always serves that application’s latest live deployment, so redeploys never need a DNS change.
#### Primary domain
An application answers on its free address and every domain you connect. Make one of them primary and the others redirect to it, so visitors and search engines see one address. Open the domain's menu (⋯) on the application's Domains tab and choose Make primary, or use Make primary under Primary and redirect in the domain's Settings.
Every other address of the application, the free .rimbra.live address included, then answers with a 308 to the primary. The path and query string are kept, so https://yourcompany.com/pricing?ref=x goes to https://www.yourcompany.com/pricing?ref=x.
The redirects start only once the primary's DNS is live, so a domain that is still waiting on DNS never takes the app's other addresses down. An application has one primary at a time; making another domain primary moves it. Choose Remove primary to serve the app on every address again.
Without a primary, search engines still list the right address. As soon as one of your domains is live, the free .rimbra.live address keeps working but answers with X-Robots-Tag: noindex and a canonical link to your domain, so Google shows yourcompany.com, not yourapp.rimbra.live. Old .rimbra.live results drop out the next time Google crawls them.
Wildcards: A wildcard domain (*.yourcompany.com) can't be primary or redirect. Its subdomains keep serving the application.
#### Redirect one domain
To send a single domain somewhere else, like the root domain to www, open it under Hosting → Domains, go to Settings and use Redirect this domain. Pick another address of the same application (its free address, or a connected domain that doesn't redirect itself), choose 308 Permanent or 301 Moved permanently, and Save. The path and query string are kept.
Pick Don't redirect to stop. The primary domain can't redirect, and a domain that redirects can't be made primary until its redirect is off. A domain that another one redirects to can't redirect itself, so redirects never chain or loop. A redirect to a connected domain starts once that domain is live.
#### What the dashboard sends
Both settings are a PATCH to /api/domains/{id} on the signed-in dashboard API. Send primary, or redirectTo with redirectStatus (301 or 308; 308 when left out). An empty redirectTo stops the redirect. The answer is the domain with its primary, redirectTo and redirectStatus fields, and visitors get the new redirects within 30 seconds.
```http
PATCH /api/domains/{id}
{ "primary": true }
PATCH /api/domains/{id}
{ "redirectTo": "www.yourcompany.com", "redirectStatus": 301 }
PATCH /api/domains/{id}
{ "redirectTo": "" }
```
#### Or let Rimbra run all of its DNS
Instead of adding one record, you can point the domain’s nameservers at Rimbra. Then every record (email, verification, subdomains) is managed on the domain’s DNS tab. The Nameservers tab lists both, with copy buttons, and shows whether the domain uses them yet.
| Nameserver |
| --- |
| ns1.rimbra.app |
| ns2.rimbra.app |
Remove the others: At your registrar, keep only these two. Nameserver changes can take a few hours to spread; the domain page notices on its own.
#### DNSSEC
DNSSEC signs your domain’s DNS answers, so a resolver can tell they really came from Rimbra and weren’t changed on the way. It works for domains on Rimbra nameservers.
On the domain’s Nameservers tab, choose Turn on under DNSSEC. Rimbra signs the domain straight away and shows a DS record: a key tag, algorithm 13, digest type 2 and a digest. Add those four values at your registrar, usually under DNSSEC or DS records. If your registrar asks for the public key instead, the page shows that too. The page says Active once your registrar publishes the record.
Switch the nameservers to Rimbra first and add the DS record after, never the other way round.
Turning it off: Remove the DS record at your registrar first. Rimbra keeps signing until the record has been gone for two days, then stops, so the domain keeps working while old answers expire.
#### Manage records
On Rimbra nameservers, or for a domain bought here, the DNS tab lists every record with a copy button, and Add a record supports these types:
- Rimbra adds two records so the domain reaches your application: an A record on @ and a CNAME on www. Delete either one to use your own instead.
- Point back to Rimbra brings Rimbra’s records back and removes your own A, AAAA, CNAME, ALIAS and ANAME records on @ and www. Email and verification records stay.
- Hosting the site somewhere else? Point @ with an ALIAS and www with a CNAME at that host, and add both names in that host’s dashboard so it can issue certificates for them.
| Type | Use it for |
| --- | --- |
| A / AAAA | Point a name at an IPv4 or IPv6 address |
| CNAME | Point a subdomain at another hostname |
| ALIAS / ANAME | Point the domain itself (@) at a hostname, where a CNAME isn’t allowed. Only on Rimbra nameservers |
| MX | Receive email |
| TXT | Verification, SPF, DKIM and DMARC |
### DNS and HTTPS
https://rimbra.app/docs/dns
How the domain checks work and what to do when a domain doesn’t go live.
#### The three checks
| Check | What it means |
| --- | --- |
| DNS | The domain points at Rimbra |
| SSL certificate | A certificate is issued and renews on its own |
| Site | Your application answers on the domain |
#### If it isn’t going live
- Points elsewhere: an old A, AAAA or CNAME record is still there. Delete it.
- Not set up yet: the record isn’t visible yet. Check the name and value, then wait a few minutes.
- Certificate issuing: DNS is right and the certificate is on its way. On Cloudflare, switch the record to DNS only.
- Site not responding: the domain works but the application isn’t live. Check its latest deployment.
- Redirects to another address: the application has a primary domain, or this domain has its own redirect. Check Primary and redirect in the domain's Settings.
- SSL error when the domain points at another host: that host has no certificate for the name yet. Add the exact name (with and without www) in that host’s dashboard.
#### Domains bought on Rimbra
Rimbra manages DNS for domains bought here. If one stops resolving, open the domain under Hosting → Domains, go to Settings and choose Re-apply DNS records. Your own records stay.
### Send email from your app
https://rimbra.app/docs/emails
Receipts, sign-in links, alerts and newsletters from your own domain, through one API.
#### Set up in four steps
1. Add your domain in Emails → Domains and publish the DNS records it shows.
2. Wait for the domain to show Verified. The page checks on its own.
3. Create a Live key in Emails → API Keys and store it on your server.
4. Send your first email with the API, then follow it in the email log.
#### Where things are
| Page | Use it to |
| --- | --- |
| Emails | See every message, its status and why one failed |
| Broadcasts | Send one email to a list of people |
| Domains | Add and verify the domains you send from |
| Metrics | Track delivery rate and volume |
| Templates | Save reusable emails with variables |
| API Keys | Create and revoke keys for your apps |
#### Your first email
```bash
curl -X POST https://api.rimbra.app/api/marketing/emails/send \
-H "Authorization: Bearer $RIMBRA_EMAILS_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "hello@yourcompany.com",
"to": "ada@example.com",
"subject": "Welcome to Yourcompany",
"html": "Thanks for signing up, Ada.
"
}'
```
### Verify a sending domain
https://rimbra.app/docs/email-domains
Prove you own the domain so inboxes trust your mail and it lands outside spam.
#### Add your domain
1. Open Emails → Domains and choose Add domain.
2. Enter the domain you send from, like yourcompany.com.
3. The domain page lists the records to add. Click a name or value to copy it.
4. Add each record where the domain’s DNS is managed.
5. Leave the page open or come back later. It re-checks every 20 seconds and shows Verified when DKIM is found.
#### The records
Use the exact values shown on your domain page. The DKIM key is unique to your domain.
| Record | Type | Name | Why |
| --- | --- | --- | --- |
| DKIM | TXT | sb._domainkey | Required. Signs each email so inboxes know it is really from you |
| Return path | MX | mail | Bounces come back to your domain |
| Return path SPF | TXT | mail | Removes the “via” label in Gmail |
| DMARC | TXT | _dmarc | Recommended. Protects your domain from spoofing |
#### Which addresses can send
Once a domain is verified, any address at it can send, such as hello@, receipts@ or no-reply@yourcompany.com. Addresses at domains you haven’t verified are rejected.
#### Tips
- On Cloudflare, keep every email record on DNS only (grey cloud).
- A name can only have one SPF record. If you already have one, merge them into a single record.
- Some registrars add your domain to the name automatically. If so, enter sb._domainkey, not sb._domainkey.yourcompany.com.
### The Email API
https://rimbra.app/docs/email-api
Create a key and send email from your server.
#### Create a key
1. Open Emails → API Keys and choose Create key.
2. Name it after the app that uses it, like Checkout service.
3. Pick the access: Send only for apps that just send, Full access for tools that also read the log, Read only for dashboards.
4. Pick Live to deliver, or Test to log emails without delivering them while you build.
5. Copy the key. It is shown once.
#### Send an email
POST https://api.rimbra.app/api/marketing/emails/send with your key as a Bearer token.
```javascript
const res = await fetch("https://api.rimbra.app/api/marketing/emails/send", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.RIMBRA_EMAILS_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
from: "Yourcompany ",
to: ["ada@example.com"],
subject: "Your receipt",
html: "Thanks for your order.
",
text: "Thanks for your order.",
}),
})
const message = await res.json() // { id, status: "queued", ... }
```
#### Fields
| Field | Required | Notes |
| --- | --- | --- |
| from | Yes | An address at a verified domain. "Name " works too |
| to | Yes | One address, or an array of addresses |
| subject | Yes, unless template_id | Up to 998 characters |
| html or text | One of them, unless template_id | Send both for the best inbox placement |
| template_id | No | Send a saved template instead of subject and body |
| variables | With template_id | Values for the template’s {{variables}} |
#### Responses
| Code | Meaning |
| --- | --- |
| 202 | Accepted and queued. Follow it in the email log |
| 400 | A field is missing or invalid |
| 401 | The key is missing, wrong or revoked |
| 403 | The key is read only |
| 422 | Test key, unverified from address, unknown template or missing variable |
| 429 | Over 60 sends per minute for this key |
Keep keys on the server: Store the key in an environment variable such as RIMBRA_EMAILS_KEY. Never put it in browser or mobile code. Use one key per app so you can revoke one without touching the rest.
### Templates
https://rimbra.app/docs/email-templates
Write an email once and send it with different names, amounts and links each time.
#### Create a template
1. Open Emails → Templates and choose New template.
2. Give it a name, a subject and a body in HTML or plain text.
3. Wrap the parts that change in double braces, like {{name}} or {{amount}}.
4. Fill in the preview values to check how it looks, then save.
5. Copy the template ID from the template page.
#### Send it
Send this body to the same send endpoint. Every variable in the subject or body must have a value, or the request is rejected with 422 and nothing is sent.
```json
{
"from": "receipts@yourcompany.com",
"to": "ada@example.com",
"template_id": "tpl_…",
"variables": {
"name": "Ada",
"amount": "₦45,000"
}
}
```
#### Good to know
- Variable names can use letters, numbers, dots, dashes and underscores.
- Values are escaped in HTML templates, so user input can’t inject markup.
- Editing a template keeps its ID. Duplicate makes a new one.
- Deleting a template makes requests that use its ID fail.
### Broadcasts
https://rimbra.app/docs/email-broadcasts
Send one email, like an update or a newsletter, to a list of people.
#### Write a broadcast
1. Open Emails → Broadcasts and choose New broadcast.
2. Pick the from address from your verified domains.
3. Paste the recipients, separated by new lines, commas or spaces. Duplicates are removed.
4. Write the subject and message, or start from a template. The preview updates as you type.
5. Choose Save draft to finish later, or Send.
#### Sending
Rimbra asks you to confirm, then queues one email per recipient. Each one appears in the email log with its own status. A sent broadcast can’t be edited or unsent.
Only email people who asked: Sending to bought or scraped lists hurts your domain’s reputation and can get your mail sent to spam for everyone.
### Email log and metrics
https://rimbra.app/docs/email-log
Follow every message and see how your mail is doing.
#### The email log
Emails shows each message with its recipient, sender, subject, status and time. Search by recipient, sender or subject, filter by status or date, and export what you see as CSV. Open a message to see its full details and why it failed.
#### Statuses
| Status | Meaning |
| --- | --- |
| Queued | Accepted and waiting to send |
| Sending | Being handed to the recipient’s mail server |
| Delivered | The recipient’s mail server accepted it |
| Failed | Couldn’t be delivered. The message shows the reason, like a mailbox that doesn’t exist |
#### Metrics
Metrics shows your delivery rate, daily volume and a breakdown by sending domain over 7, 15 or 30 days. The log and metrics cover your most recent 50 messages.
### Land in the inbox
https://rimbra.app/docs/email-deliverability
What decides whether your mail reaches the inbox, and how to keep it there.
#### The basics
- Verify your domain and add every record, including the return path and DMARC.
- Send from an address people recognise, like hello@ or receipts@ your own domain.
- Send both html and text versions.
- Only email people who signed up or bought something.
#### If emails fail
1. Open the failed message in the email log and read the reason.
2. If the from address is rejected, check its domain shows Verified in Domains.
3. If you get 422 for a test key, create a Live key.
4. If the reason is a missing mailbox, remove that address from your list.
#### If emails land in spam
- Check every record on the domain page shows Found, not just DKIM.
- Warm up a new domain by sending small volumes first, then grow.
- Avoid subject lines in all capitals and messages that are a single image.
### Social notifications
https://rimbra.app/docs/whatsapp
Link a WhatsApp number, then message your users from the dashboard or your code, now or on a schedule.
#### Turn it on
1. Open Social notifications and choose Turn on Social notifications. An API key is created for you.
2. On Numbers, choose Link a number.
3. On your phone, open WhatsApp → Settings → Linked devices → Link a device and scan the QR code. Or choose Use pairing code, enter the number on that phone, and type the 8-character code on the phone.
4. The number shows Connected and stays linked.
#### No spare number? Get one
Under Get a number you can buy a number in most countries to set up a new WhatsApp or WhatsApp Business account with. It is a one-time price, shown for each country and paid with points.
1. Open Get a number and choose Buy next to a country.
2. After checkout the number shows up with a 5-minute timer.
3. On your phone, set up WhatsApp with that number and ask for the code by SMS. The code appears on the page.
4. Enter the code in WhatsApp, then link the number under Numbers.
#### Get codes by email or WhatsApp
You don't need to keep the page open. When a number receives a code, Rimbra emails it to your account address, with the code shown large and a link back to the number. Rented numbers send one email for each message they receive.
To get codes on WhatsApp too, add your WhatsApp number under Get a number. To stop the emails, switch Email off there. Each code is sent once.
#### Good to know
Each number receives one sign-up code. Your WhatsApp account stays on your phone afterwards, but WhatsApp can’t send that number a second code, so turn on two-step verification in WhatsApp right away.
Some numbers never receive WhatsApp’s code. Choose Cancel on the number, or wait 5 minutes, and you get a new number in the same country for free. If the replacement doesn’t get a code either, contact support with the reference shown and we’ll sort out a refund.
#### The tabs
| Tab | Use it to |
| --- | --- |
| Numbers | Link, rename or remove numbers |
| Get a number | Buy a number to set up WhatsApp on, if you don’t have a spare SIM |
| Inbox | Read and reply to conversations |
| Compose | Send a message from any of your numbers to a number, picked contacts or a tag, now or later |
| Contacts | Name and tag contacts, import a list and manage opt-outs |
| Webhooks | Get messages and delivery results on your server |
| Usage | See sent, received and failed messages per day |
| API | Your secret key and the setup checklist |
#### Tag your contacts
Contacts are added when someone messages your numbers or gets a message from them. Open Social notifications → Contacts and pick a contact to add tags, like customer or vip, under Tags. Filter the list by a tag with Any tag at the top.
Tags are lowercase and use letters, digits, spaces, - and _, up to 32 characters each. A contact can have up to 20.
#### Import contacts
1. On Contacts, choose Import contacts.
2. Choose a CSV file or paste rows. Each row needs a phone number with its country code; a name and tags are optional.
3. Check what each column is: Phone, Name, Tags or Skip. A header row like phone,name,tags is read for you.
4. Choose Import. New numbers are added; numbers you already have keep their opt-out, take the new name and gain the new tags.
Only people who agreed: Import only people who agreed to hear from you on WhatsApp. Up to 10,000 rows at a time.
#### Send now or later
A scheduled send goes out from the number you picked, one person at a time, about a second apart so WhatsApp doesn't flag the number. Anyone who opts out before their turn is skipped. If the number is disconnected when the send starts, the rest are marked failed, so keep it linked.
1. Open Compose and pick who it goes to: A number, Contacts you tick, or A tag (shown once a contact has one). Contacts who opted out are left out.
2. Write the message. {{name}} becomes each person's first name, or there when the contact has no name. Add an attachment link if you like.
3. Under When, choose Now, or Later and pick a date and time up to 90 days ahead.
4. Choose Send, or Schedule. Scheduled sends are listed under Scheduled with Cancel until they start.
#### Phone numbers
Write numbers with the country code, like +2348012345678. If you type a local number like 08012345678, Rimbra adds the country code of the number you send from.
Keep numbers healthy: WhatsApp can restrict numbers that get reported. Message people who expect to hear from you. Your plan gives you a monthly allowance with no daily limit, shared across your numbers.
### Social notifications API
https://rimbra.app/docs/whatsapp-api
Send text and media now or on a schedule, manage tagged contacts and read conversations from your code.
#### Authentication
Send your secret key from Social notifications → API as a Bearer token. The base URL is https://api.rimbra.app/ntf/v1. The key is shown once, when the API is turned on or the key is rolled, so save it right away. Roll the key from the same page if it is lost or ever exposed; the old key stops working right away.
```bash
curl https://api.rimbra.app/ntf/v1/whatsapp/numbers \
-H "Authorization: Bearer $RIMBRA_NOTIFY_KEY"
```
#### Send a message
`to` is the recipient with country code. `from` is optional: pass one of your linked phone numbers (or its number ID from /whatsapp/numbers) to pick the sender, otherwise your most recently connected number is used. The response is the message with status queued; it becomes sent or failed within seconds.
```bash
curl -X POST https://api.rimbra.app/ntf/v1/messages \
-H "Authorization: Bearer $RIMBRA_NOTIFY_KEY" \
-H "Content-Type: application/json" \
-d '{
"channel": "whatsapp",
"from": "+2348098765432",
"to": "+2348012345678",
"content": { "text": "Your order has shipped" }
}'
```
#### Send from several numbers
A workspace can link up to 10 WhatsApp numbers, for example one for orders and one for support. Each send picks its number with `from`; GET /whatsapp/numbers lists them with their IDs, phone numbers and status. A number that isn’t connected answers 422, so reconnect it in Social notifications → Numbers.
#### From Node.js
Node 18 and newer have fetch built in. Keep the key in an environment variable on your server, never in browser code.
```js
const res = await fetch("https://api.rimbra.app/ntf/v1/messages", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.RIMBRA_NOTIFY_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ channel: "whatsapp", to: "+2348012345678", content: { text: "Your order has shipped" } }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.detail);
```
#### From Python
```python
import os, requests
r = requests.post(
"https://api.rimbra.app/ntf/v1/messages",
headers={"Authorization": f"Bearer {os.environ['RIMBRA_NOTIFY_KEY']}"},
json={"channel": "whatsapp", "to": "+2348012345678", "content": {"text": "Your order has shipped"}},
timeout=30,
)
r.raise_for_status()
print(r.json()["id"], r.json()["status"]) # queued → sent or failed
```
#### Send media
Add media_url, an https link Rimbra can download, and media_type: image, video, audio or document. The text becomes the caption.
```json
{
"channel": "whatsapp",
"to": "+2348012345678",
"content": {
"text": "Your invoice",
"media_url": "https://example.com/invoice-1042.pdf",
"media_type": "document"
}
}
```
#### Schedule a message
Add send_at, an RFC3339 time with a timezone, at least a minute and at most 90 days ahead. With send_at, `to` can be one number or a list of up to 10,000, and `label` (up to 120 characters) names the send in the dashboard. {{name}} in the text becomes each contact's first name, or there when the contact has no name.
The answer is a scheduled_message with status scheduled. When it is due, Rimbra sends to each recipient in turn, skips anyone who opted out, and fires message.sent or message.failed for each one. The whole list is checked against your monthly allowance when you schedule it; if it doesn't fit and your points can't cover the rest, the request answers 429.
```bash
curl -X POST https://api.rimbra.app/ntf/v1/messages \
-H "Authorization: Bearer $RIMBRA_NOTIFY_KEY" \
-H "Content-Type: application/json" \
-d '{
"channel": "whatsapp",
"to": ["+2348012345678", "+2348098765432"],
"send_at": "2026-11-20T09:00:00+01:00",
"label": "Black Friday",
"content": { "text": "Hi {{name}}, the sale starts now." }
}'
# {"id": "sch_…", "object": "scheduled_message", "status": "scheduled", "send_at": "…", "to": […], "sent": 0, "failed": 0, "skipped": 0, …}
```
#### List and cancel scheduled sends
GET /messages/scheduled lists sends that are waiting or sending, soonest first. Add ?status=all to include the last 50 that finished, with their sent, failed and skipped counts. DELETE /messages/scheduled/{id} cancels one that hasn't started and answers 204; one that is already sending or sent answers 409.
```bash
curl https://api.rimbra.app/ntf/v1/messages/scheduled?status=all -H "Authorization: Bearer $RIMBRA_NOTIFY_KEY"
curl -X DELETE https://api.rimbra.app/ntf/v1/messages/scheduled/sch_123 -H "Authorization: Bearer $RIMBRA_NOTIFY_KEY"
```
#### Tag contacts
Every contact has a tags list. PATCH /contacts/{id} with tags replaces it. Tags are lowercased and keep letters, digits, spaces, - and _, up to 32 characters; a contact keeps up to 20. GET /contacts?tag=vip lists the contacts with that tag, and q= searches numbers and names.
```bash
curl -X PATCH https://api.rimbra.app/ntf/v1/contacts/cnt_123 \
-H "Authorization: Bearer $RIMBRA_NOTIFY_KEY" \
-H "Content-Type: application/json" \
-d '{"tags": ["customer", "vip"]}'
curl "https://api.rimbra.app/ntf/v1/contacts?tag=vip" -H "Authorization: Bearer $RIMBRA_NOTIFY_KEY"
```
#### Import contacts
POST /contacts/import takes CSV text (Content-Type: text/csv), JSON {"csv": "…"}, JSON {"contacts": [{phone, name, tags}]}, or a bare array of those. Up to 10,000 rows and 5 MB a request. In CSV, a header row names the columns (phone, name, tags); without one the order is phone, name, tags. Tags in one cell can be split with , ; | or /.
Numbers need a country code. New numbers are added. Numbers you already have keep their opt-out, take a name you send, and gain the tags you send. The answer counts what happened and lists up to 100 rows that were refused.
```bash
curl -X POST https://api.rimbra.app/ntf/v1/contacts/import \
-H "Authorization: Bearer $RIMBRA_NOTIFY_KEY" \
-H "Content-Type: text/csv" \
--data-binary @contacts.csv
# {"added": 120, "updated": 8, "skipped": 3, "error_count": 1,
# "errors": [{"row": 42, "phone": "0801", "error": "Not a valid phone number with a country code"}]}
```
#### Endpoints
| Endpoint | Does |
| --- | --- |
| POST /messages | Send a text or media message, or schedule one with send_at |
| GET /messages, GET /messages/{id} | Recent messages and one message’s status |
| GET /messages/scheduled, DELETE /messages/scheduled/{id} | Scheduled sends, and cancel one |
| GET /whatsapp/numbers | Your numbers, their status and how many they sent today |
| GET /inbox/threads, GET /inbox/threads/{id}/messages | Conversations |
| GET /contacts?q=&tag=, PATCH /contacts/{id} | Contact names, tags and opt-outs |
| POST /contacts/import | Add or update up to 10,000 contacts from CSV or JSON |
| GET /usage?from=YYYY-MM-DD&to=YYYY-MM-DD | Daily sent, received and failed |
| GET, POST, PATCH, DELETE /webhooks | Webhook endpoints |
#### Limits and errors
Contacts who reply STOP or UNSUBSCRIBE are opted out automatically. Turn messaging back on for them in Contacts.
| Code | Meaning |
| --- | --- |
| 401 | The key is missing or wrong |
| 400 | Something in the request is missing or invalid, e.g. the number has no country code or send_at is too soon. `detail` says what |
| 404 | The `from` number isn’t one of your linked numbers |
| 422 | No connected number to send from, the `from` number is disconnected, the recipient isn’t on WhatsApp, or they opted out. Linking an 11th number also answers 422 |
| 429 | Your workspace used its monthly social notifications allowance. Buy more in Billing → Usage or upgrade |
### Webhooks
https://rimbra.app/docs/webhooks-guide
Get new messages, delivery results and number changes on your server as they happen.
#### Events
Each request is a POST with a JSON body containing event, created_at and data.
| Event | Sent when |
| --- | --- |
| message.received | Someone messages one of your numbers |
| message.sent | Your message was handed to WhatsApp |
| message.failed | A message couldn’t be sent. data.error says why |
| number.connected | A number finished linking |
| number.disconnected | A number was logged out from the phone |
#### Add an endpoint
1. Open Social notifications → Webhooks and choose Add endpoint.
2. Enter your https URL and pick the events.
3. Copy the signing secret (whsec_…).
4. Choose Send test event and check Recent deliveries.
#### Verify the signature
Every request has X-Rimbra-Event, X-Rimbra-Timestamp and X-Rimbra-Signature headers. The signature is sha256= followed by the HMAC-SHA256 of "{timestamp}.{raw body}" with your signing secret.
```javascript
import crypto from "crypto"
export function verifyRimbra(rawBody, headers, secret) {
const timestamp = headers["x-rimbra-timestamp"]
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex")
const given = headers["x-rimbra-signature"] || ""
return given.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected))
}
```
#### Retries
- Answer with a 2xx status within 10 seconds.
- Failed deliveries are retried up to three times, waiting longer each time.
- Every attempt shows under Recent deliveries with the response code.
- Pause an endpoint with its switch while you fix your server.
Answer fast: Store the event and return 200 straight away, then do the slow work in the background.
### The Rimbra agent
https://rimbra.app/docs/agent
Why a deploy failed, one-click fixes, code fixes that redeploy themselves, and Ask AI on every page.
#### When a deploy fails
The failed deploy's page says what went wrong in one sentence, with the log line that shows it. Common problems are matched straight away; for the rest, the agent reads the end of the build log, your settings, the names of your variables and your package files. It never sees variable values: they are removed before anything is sent.
- Setting problems (Node version, install or build command, port, a missing variable) come with a button that changes the setting and deploys again.
- A missing secret asks you to type it. It is saved on the application like any variable, and the agent never sees it.
- Your live site keeps running the last good build the whole time.
#### Fix with agent
1. On a failed deploy from GitHub, choose Fix with agent. The agent panel opens and you watch it work.
2. It reads the repository at the failed commit, finds the cause and changes what's needed.
3. It puts the fix into the branch the application deploys from, and a normal deploy starts. The page takes you to the new build.
4. If that build still fails, it gets its own diagnosis and you can fix again.
Repositories that need a review: If your branch requires a review before changes go in, the agent leaves the fix as a pull request on GitHub instead. Merge it there and Rimbra deploys it.
#### GitHub access
To put a fix in, the Rimbra GitHub app needs read and write access to code and pull requests. If GitHub asks you to approve updated permissions for Rimbra, accept them once and Fix with agent works from then on.
#### Ask AI
Ask AI is at the top of every dashboard page (Cmd+I). Ask in plain words: why a deploy failed, where a setting is, whether a domain is set up right, add a database, set up a cron job. On an application's page it starts focused on that application. The Agent page shows the same chats full width.
- Looking things up runs straight away.
- Anything that changes something shows a card with what it will do. It runs only when you approve it.
- It can't pay for anything, change billing or manage your team.
- Logs, files and messages it reads are treated as data: instructions written inside them are never followed.
#### Plans
Every plan includes a monthly agent allowance. Diagnoses matched to a known problem are free and don't count. Code fixes come with Launch and up. The allowance resets on the 1st, and the dashboard shows how much of it you've used.
### Rimbra MCP server
https://rimbra.app/docs/mcp
Connect Claude, ChatGPT, Cursor or any MCP client to your workspace so it can deploy, read logs, run databases and send messages.
#### What an agent can do
Rimbra runs an MCP server (Model Context Protocol, the standard most AI agents speak) at https://app.rimbra.app/mcp. A connected agent works in one workspace with the same plan limits and team roles as a person. It can never pay for anything, buy domains, or change billing or the team.
| Area | Tools |
| --- | --- |
| Workspace | whoami, list_team, contact_support |
| Applications | list_projects, get_project, create_project, update_project_settings, list_github_repos |
| Deploy | deploy_project, get_deployment, cancel_deployment, get_runtime_logs, rollback_project |
| Variables | list_env_vars, set_env_var |
| Cron jobs | list_cron_jobs, create_cron_job, update_cron_job, delete_cron_job, run_cron_job, get_cron_runs, save_cron_secret |
| Databases | list_databases, create_database, connect_database, query_database, list_backups, backup_database, restore_backup |
| Domains | list_domains, add_domain, search_domains |
| Email API | list_email_domains, add_email_domain, send_email, list_emails, list_email_api_keys, create_email_api_key, delete_email_api_key |
| Social notifications | list_whatsapp_numbers, send_whatsapp, get_whatsapp_api_key, rotate_whatsapp_api_key |
#### Claude Code
Install the Rimbra plugin. It adds the connection and a skill that teaches Claude how Rimbra works.
```
/plugin marketplace add AdejareAkolawole/rimbra-agents
/plugin install rimbra@rimbra
```
With a key instead: Make a key on the MCP page in the dashboard, then run: claude mcp add --transport http rimbra https://app.rimbra.app/mcp --header "Authorization: Bearer YOUR_KEY"
#### Claude.ai and Claude Desktop
1. Open Settings → Connectors → Add custom connector.
2. Name it Rimbra and paste https://app.rimbra.app/mcp.
3. Choose Connect, sign in to Rimbra, pick the workspace and access, and choose Allow.
#### ChatGPT
1. Open Settings → Apps & Connectors, and turn on developer mode under Advanced.
2. Create a connector with the address https://app.rimbra.app/mcp and OAuth sign-in.
3. ChatGPT sends you to Rimbra to approve it.
Plans: Custom connectors need a ChatGPT plan that allows them.
#### Cursor and other clients
Use Add to Cursor on the MCP page in the dashboard, or add this to ~/.cursor/mcp.json. Any client that speaks Streamable HTTP works the same way.
```json
{ "mcpServers": { "rimbra": { "url": "https://app.rimbra.app/mcp" } } }
```
#### Access and keys
- Signing in (OAuth) is best: nothing to copy, and access tokens last an hour and refresh themselves.
- Keys are for agents that can’t sign in, like a terminal or CI. They start with rmb_ and are shown once.
- Developer access can deploy and change things. Viewer access only reads.
- An agent never gets more access than the person who connected it. A team Viewer can only connect Viewer agents.
- See and disconnect every agent and key on the MCP page in the dashboard.
#### For agents that read docs
A short plain-text guide to Rimbra for language models lives at /llms.txt.
### Account and billing
https://rimbra.app/docs/account
Your sign-in, profile, workspaces, plan, usage and invoices.
#### Your account
- Sign in, or make an account, at app.rimbra.app/sign-in.
- Your account comes with its own workspace for everything you create. You can make more workspaces and join teams, and switch between them.
- Signing out ends the session on that browser. It never deletes anything.
#### Profile photo
Open Settings → Profile and add a photo under Photo: a PNG, JPEG or WebP up to 2 MB. Rimbra crops it square and keeps a small copy. Until you add one, your initials show. The photo belongs to you, not the workspace, so it shows in every workspace you are in. Remove it from the same place.
#### Workspaces
Each workspace has its own applications, team, plan and bill, and a new one starts empty on the Free plan. You can create up to 3 workspaces on top of your own. Switch between them under Settings → Workspaces.
1. Open Settings → Workspaces and choose Create workspace.
2. Give it a name, up to 80 characters, and choose Create workspace.
3. You move into the new workspace as its owner. Invite people from Settings → Team.
#### Referral links
Every account has its own referral link. Open Settings → Referrals to copy it. It looks like rimbra.app/r/your-code, and adding ?ref=your-code to any rimbra.app page works the same way.
- Visits counts each browser once a day.
- Sign-ups counts accounts made within 60 days of someone opening your link, by email, GitHub or Google. The first link a browser opened is the one that counts.
- Paid users counts the people you brought in who have made at least one payment.
- When someone signs up through your link and makes their first payment, you get 100 points ($1), once per person. Points land in your balance within a few minutes and pay for any plan or add-on. Paying with points doesn’t count as a first payment. Referring yourself from a second account earns nothing.
#### Plans
| | Free | Launch | Pro | Scale |
| --- | --- | --- | --- | --- |
| Price per month | $0 | $8 | $20 | $69 |
| Applications | 2 | 15 | 50 | Unlimited |
| Databases | 1 | 5 | 15 | 40 |
| Memory shared by your apps | 1 GB | 3 GB | 8 GB | 24 GB |
| Memory per app | 512 MB | 1 GB | 2 GB | 4 GB |
| Build minutes a month | 100 | 1,500 | 4,000 | 12,000 |
| Builds at the same time | 1 | 2 | 3 | 8 |
| Earlier builds kept for rollback | 1 | 3 | 5 | 10 |
| Storage (files and databases) | 3 GB | 25 GB | 80 GB | 300 GB |
| Outbound bandwidth a month | 20 GB | 200 GB | 500 GB | 1,500 GB |
| Emails a month | 1,000 | 10,000 | 30,000 | 100,000 |
| Social notifications a month | 1,000 | 10,000 | 30,000 | 100,000 |
| Custom domains | 1 | 10 | 25 | 100 |
#### Functions, Queues, Tunnels and Buckets
These four are add-ons on every plan, Free included, and no plan includes them on its own. Captcha stays on every plan. Open one in the sidebar and, until you have its add-on, the page shows its price and what it includes with a Pay button; it unlocks as soon as the payment is confirmed. Pay from your points, by bank transfer, or ask for a USD checkout link.
- An add-on stacks with the pack, so owning both adds their allowances together.
- Past the allowance, usage is paid from your points while pay as you go is on. With it off, or with no points left, that product stops at its allowance until the next cycle or until you add points.
- Add-ons renew monthly from your points with your plan. Billing → Bill lists the ones you have and lets you cancel them.
- Without the add-on, what you made is paused: it stops serving and can't be changed, but nothing is deleted. Adding it turns everything back on at once.
| Add-on | Price a month | Included each cycle | Past that, from points |
| --- | --- | --- | --- |
| Functions | $4 | 300,000 requests, up to 5 functions | $1 per million requests |
| Queues | $3 | 500,000 messages, up to 3 queues, kept 3 days | $1 per million messages |
| Tunnels | $4 | 1 tunnel with its own subdomain, 5 GB transfer | $0.20 per GB |
| Buckets | $3 | 5 GB stored, 500,000 writes, 5 million reads | $0.03 per GB-month, $6 per million writes, $0.50 per million reads |
| Developer pack | $10 | All four, with double the allowances | The same rates |
#### Usage
Billing → Usage shows how much of each limit you have used in your current cycle. Each cycle starts on the day your workspace signed up and repeats on that date every month, so build minutes, emails, social notifications and bandwidth are counted from there. There are no daily limits. The Overview page warns you at 80% and when you reach a limit, and add-ons raise a limit straight away.
#### Paying
- Rimbra runs on points: 100 points cost $1, so the $8 Launch plan is 800 points a month. Add 500 to 500,000 points from Billing → Bill → Add points, in USD by Visa or Mastercard, or in your local currency by bank transfer. Points are added once the payment is confirmed and never expire.
- To pay in USD, choose USD when you add points and send the request. We'll email you a secure checkout link, and you're only charged when you pay from it. Each link works for 60 minutes.
- Upgrades, add-ons, numbers and mailboxes are paid with points. If you don’t have enough, Rimbra asks you to add points first. Domains can be paid with points or directly at checkout.
- Points can be spent on anything in Rimbra, but they can’t be withdrawn, moved to another account, or refunded to a card or bank account.
- Every plan, Free included, keeps what it includes each month. Past that you pay as you go from points: 40 points per 100 emails or social messages, 2 points per build minute and 10 points per GB of bandwidth, taken as you use them. Extra applications, databases and storage are added from points when you reach the limit and renew monthly.
- If your points run out, only the thing that ran out stops (sending, building, or serving past your bandwidth) until you add points, the period resets, or you upgrade. Turn pay as you go off in Billing → Bill to keep to your plan’s limits.
- Your plan and add-ons renew from your points automatically. If you don’t have enough for the renewal, we email you 3 days before. You keep the plan for 7 days after it ends, and it renews as soon as you add enough points. After that the workspace moves to Free; applications and data stay.
- New accounts can pay up to $50 a day by card for their first week, or until a first payment has cleared. Bank transfer has no such limit.
- A paid plan covers a month, or a year if you chose yearly. Billing → Bill shows the date it is paid until. We email you 3 days before; renewing adds the time on from that date.
- If you don’t renew, you keep the plan for 7 more days, then the workspace moves to Free. Nothing is deleted, and anything over the Free limits waits until you upgrade again.
- An invoice from us (for a custom plan, for example) comes by email with a link to pay by card or bank transfer from any Nigerian bank.
- Put the invoice number (for example RIM-202610-0007) in the transfer description. The invoice is marked paid within minutes of the transfer arriving and you get a receipt by email. If you forget, contact support with your bank receipt.
#### Invoices
Billing → Bill lists your invoices. Use the download button on a row, or open an invoice and choose Download PDF, to save it as a PDF named after its invoice number. The dashboard fetches it from GET /api/billing/invoices/{id}/pdf, which only returns invoices of the workspace you are in.
#### Keep your workspace safe
- Keep API keys and database URLs in environment variables, never in code.
- Use one key per app and revoke keys you no longer use.
- Rotate a key straight away if it ever ends up somewhere public.
- To stop abuse, when several new Free workspaces deploy from the same network in one day, the next new one waits until the following day. Upgrading removes the wait.