Urva Prep

Automation API

Let your automations talk to your team

Every Urva Prep business gets bot accounts for free — a stable API key your own scripts, Zapier zaps, n8n workflows, or cron jobs can use to message an employee or post an announcement. Everything lands in the app in real time, just like it came from a person.

Get your API key

01

Start your team

Sign up as the business owner — no bot key is needed to get going.

02

Open Settings → Bot accounts

In the dashboard, head to Settings and find the Bot accounts card.

03

Create a bot, copy its key

Name it (e.g. "Shift Reminder"). The raw key is shown once — stash it somewhere safe.

04

Make your first call

Use that key as a bearer token against any endpoint below.

Base URL & auth

All endpoints live under one base URL. Authenticate with a bot's key (Messaging/Announcements below) or an automation channel's key (Automations below) as a bearer token — no user login, no token refresh.

Base URL   https://backend-production-20c6.up.railway.app
Header     Authorization: Bearer <your-bot-or-channel-api-key>
Header     Content-Type: application/json

Embed object reference

Automation channels accept clean, rich embeds — shaped to match Discord's own webhook embed object as closely as reasonable, so a tool that already knows how to post a Discord embed needs little to no adaptation to post here instead. Every field is optional; send whatever your automation has.

Example request body

{
  "content": "Build #482 failed",
  "username": "CI Bot",
  "avatar_url": "https://example.com/ci-bot.png",
  "embeds": [
    {
      "title": "Build failed",
      "description": "main branch — 3 tests failing",
      "url": "https://ci.example.com/builds/482",
      "color": 15158332,
      "author": { "name": "example/repo" },
      "fields": [
        { "name": "Branch", "value": "main", "inline": true },
        { "name": "Duration", "value": "2m 14s", "inline": true }
      ],
      "footer": { "text": "example CI" },
      "timestamp": "2026-03-04T18:22:00Z"
    }
  ]
}

How it renders

CCI Botjust now

Build #482 failed

example/repo

Build failed

main branch — 3 tests failing

Branch

main

Duration

2m 14s

example CI · 3/4/2026, 6:22:00 PM

FieldTypeNotes
titlestringUp to 256 characters.
descriptionstringUp to 4096 characters.
urlstring (url)Makes the title a clickable link.
colorintegerDecimal RGB, 0–16777215 (e.g. red = 16711680).
timestampstring (ISO 8601)Shown in the footer.
author.name / .url / .icon_urlstringA small byline above the title.
footer.text / .icon_urlstringSmall text under everything else.
image.urlstring (url)Large image, shown at full width.
thumbnail.urlstring (url)Small image, shown top-right.
fieldsarray, up to 25Each: { name, value, inline? } — inline fields lay out two per row.
POST/api/bot/messages

Send a direct message

Message any employee directly. It lands in their normal Chats tab in real time, exactly like a message from a teammate. Identify the person by whichever you have on hand — their Urva Prep user id, phone number, or email.

Request body

{
  "to": { "email": "jane@example.com" },
  "body": "Your shift starts at 7am tomorrow."
}

Example (curl)

curl -X POST https://backend-production-20c6.up.railway.app/api/bot/messages \
  -H "Authorization: Bearer <your-bot-api-key>" \
  -H "Content-Type: application/json" \
  -d '{"to": {"phoneNumber": "+15551234567"}, "body": "Reminder: timesheet due Friday."}'
FieldTypeNotes
to.userIdstring (uuid)Provide exactly one of userId, phoneNumber, or email
to.phoneNumberstringMust match the employee's number on file exactly
to.emailstringCase-insensitive match
bodystring1–4000 characters

Returns 201 with the created message. The first message to a given employee starts a direct chat between your bot and them; every message after that reuses the same chat, so it reads as one ongoing conversation. A 404 means no employee matched the to field; a 401 means the key is missing, wrong, or revoked.

POST/api/bot/announcements

Post an announcement

Post a team-wide announcement — the same result as a manager posting one from the dashboard home feed. Every active employee gets notified, and it shows up in the Announcements feed with likes, comments, and read tracking, attributed to your bot's name.

Request body

{
  "title": "Store closed Monday",
  "body": "Enjoy the long weekend — we reopen Tuesday at 8am."
}

Example (curl)

curl -X POST https://backend-production-20c6.up.railway.app/api/bot/announcements \
  -H "Authorization: Bearer <your-bot-api-key>" \
  -H "Content-Type: application/json" \
  -d '{"title": "Safety refresher", "body": "New lift procedure posted — please review before your next shift."}'
FieldTypeNotes
titlestring1–150 characters
bodystring1–4000 characters

Returns 201 with the created announcement (id, title, body, createdAt, createdBy, and its like/comment counts). It's attributed to the bot's name, so employees see who posted it. A 401 means the key is missing, wrong, or revoked.

POST/api/automation/messages

Post a message to a channel

Post a message — plain text, one or more rich embeds, or both — to an automation channel. Create a channel in the dashboard's Automations tab to get its API key. Only the employees you've given view access to (plus owner/admin) see it; everyone else never knows it exists.

Request body

{
  "content": "Build #482 failed",
  "username": "CI Bot",
  "embeds": [
    {
      "title": "Build failed",
      "description": "main branch — 3 tests failing",
      "color": 15158332,
      "fields": [
        { "name": "Branch", "value": "main", "inline": true },
        { "name": "Duration", "value": "2m 14s", "inline": true }
      ]
    }
  ]
}

Example (curl)

curl -X POST https://backend-production-20c6.up.railway.app/api/automation/messages \
  -H "Authorization: Bearer <your-channel-api-key>" \
  -H "Content-Type: application/json" \
  -d '{"content": "Deploy finished ✅", "embeds": [{"title": "urva-prep@2.4.0", "color": 3066993}]}'
FieldTypeNotes
contentstringOptional, up to 2000 characters. At least one of content or embeds is required.
usernamestringOptional per-message sender name override, up to 80 characters.
avatar_urlstring (url)Optional per-message sender avatar override.
embedsarrayOptional, up to 10. See the embed object reference above.

Returns 201 with the created message. Everyone with access to the channel is notified and sees it live if they have the channel open. A 401 means the key is missing, wrong, or revoked; a 400 means the payload failed validation (e.g. neither content nor embeds provided).

POST/api/automation/webhooks/:channelId/:token

Post via a Discord-compatible webhook URL

Same as above, but shaped as a URL instead of a bearer header — literally the same layout as a Discord webhook URL (.../webhooks/{id}/{token}). If a tool's only integration option is "paste a Discord webhook URL," paste this instead and it works unmodified. Find the full URL (with your channel's id and key already filled in) on the channel's settings panel in the dashboard.

Example (curl)

curl -X POST https://backend-production-20c6.up.railway.app/api/automation/webhooks/<channel-id>/<your-channel-api-key> \
  -H "Content-Type: application/json" \
  -d '{"content": "Deploy finished ✅", "embeds": [{"title": "urva-prep@2.4.0", "color": 3066993}]}'
FieldTypeNotes
:channelIdstring (uuid)The channel's id, shown in the dashboard's Automations tab.
:tokenstringThe channel's raw API key.
content / username / avatar_url / embedsIdentical body shape to the bearer-key endpoint above.

Same response shape as the bearer-key endpoint. A 401 means the key in the URL is wrong/revoked, or doesn't match the :channelId segment.

Ready to connect your automations?

Start your team, then set up a bot in Settings in under a minute.

Start your team