Skip to content
Runqora

Set up Runqora

Runqora connects to what your application already runs. Nothing moves: your users stay in your database and your files in your bucket. These are the pieces, and what each one needs from you.

Start here

  1. Create an account and an organization. The organization holds your projects, your teammates and billing.
  2. Create a project for each application. It comes with Development, Preview and Production environments, each with its own connections and keys.
  3. Follow the project's setup guide: choose the environment you can test against today, connect the services your application already uses, and create an API key for your server.

Create a free account to begin. One project is free for as long as you like.

Install the SDK

The runqora package has no dependencies and uses only fetch and Web Crypto, so it runs in Node.js, Bun, Deno and edge runtimes. Use it on your server: the API key must never reach a browser.

Install
bun add runqora

Create an API key in your project and keep it in RUNQORA_API_KEY. A key belongs to one environment and only does what its scopes allow.

Create the client
import { Runqora } from "runqora";
const runqora = new Runqora({ apiKey: process.env.RUNQORA_API_KEY });

The source and full reference are on GitHub.

API keys

Give each customer or service that calls your API a key of its own, with the scopes it needs and an expiry if you like. When a request arrives, your server asks Runqora whether the key is valid. Revoking one key leaves the others working.

The key your server uses for this needs “Verify API keys” switched on.

Verify a key
import { Runqora } from "runqora";
const runqora = new Runqora({ apiKey: process.env.RUNQORA_API_KEY });
const result = await runqora.keys.verify(key, { scopes: ["orders:read"] });if (!result.valid) {  return new Response("Unauthorized", { status: 401 });}// result.keyId, result.name and result.scopes tell you who is calling.

Webhooks

Add the endpoints of your customers or your own services, then send events from your server. Runqora signs each delivery, retries failures, and keeps the history so you can replay one. Sending needs a key with “Send webhooks” switched on.

Send an event
import { Runqora } from "runqora";
const runqora = new Runqora({ apiKey: process.env.RUNQORA_API_KEY });
await runqora.webhooks.send({  type: "order.created",  data: { orderId: "o_1042", total: 4200 },});

Whoever receives them checks the signature with the endpoint's secret:

Receive an event
import { verifyWebhook } from "runqora";
export async function POST(request: Request) {  const event = await verifyWebhook({    payload: await request.text(),    headers: request.headers,    secret: process.env.RUNQORA_WEBHOOK_SECRET,  });
  // event.type and event.data are yours. event.id is the same on every  // retry and replay, so use it to skip an event you already handled.  return new Response(null, { status: 204 });}

Uptime monitors

Add the HTTPS address of your site or a health endpoint of your API. Runqora requests it on a schedule, records how it answered and how fast, and emails the people who should know when it stops answering and when it recovers.

Cron monitors

Create a monitor for each scheduled job, saying how often it runs and how late it may be. The job reports when it succeeds, from a crontab line or from its own code; if a report does not arrive in time, you get an email.

From a crontab
# Report only when the job succeeded.0 3 * * * /usr/local/bin/nightly-export && curl -fsS --retry 3 https://api.runqora.dev/v1/heartbeat/<your monitor's token>

The monitor's page shows its own address and a version for the SDK, runqora.monitoring.heartbeat().

Storage

Connect a Cloudflare R2 or Amazon S3 bucket (or any S3-compatible service, through a custom endpoint) in the project's integrations, once per environment. You need the bucket's name, where it is, and an access key. Use a key that can only reach that bucket.

Then browse, upload and delete files from the dashboard, and let your application hand out signed upload addresses so files go straight from the browser to your bucket.

Authentication

Runqora shows and manages the users of your application without copying them. If you use Better Auth (1.6.13 or later), add the plugin, then connect Better Auth in your project with your auth address (usually your site followed by /api/auth) and the same secret, at least 32 characters long.

Better Auth
import { betterAuth } from "better-auth";import { runqora } from "runqora/better-auth";
export const auth = betterAuth({  // ...your existing configuration  plugins: [runqora({ secret: process.env.RUNQORA_AUTH_SECRET })],});

Banning users and showing roles need Better Auth's admin plugin, and organizations its organization plugin. Without them, Runqora hides those features.

With any other auth system, mount a small handler that answers Runqora's signed requests. Listing and reading users is required; every other function you add, such as banning users or listing sessions, switches the matching feature on.

Any other auth system
import { createAuthBridge } from "runqora/auth";
const bridge = createAuthBridge({  basePath: "/api/runqora",  secret: process.env.RUNQORA_AUTH_SECRET,  users: {    get: (id) => db.users.find(id),    list: ({ limit, offset, search }) => db.users.page({ limit, offset, search }),  },});
export const GET = bridge;export const POST = bridge;