Integration guide
From installing the app to your first alert landing on a teammate's phone: about five minutes.
1 · Four steps, start to finish
Install, then create a project
Sign in with a six-digit email code — no password to invent. A project is the unit that alerts and members belong to.
Generate a push key
Project Settings → Push Key → Generate → Copy. What you get is a complete URL, not a bare token.
POST to it
One HTTP request from a terminal, a CI job, a cron entry or a running agent. A title and a Markdown body.
Invite the team
Project Settings → Invite Members. They scan the QR code and join; from then on every push reaches all of them.
2 · The push URL
One URL per project. It carries the project ID in the path and the key in the query string, so it works anywhere a URL works — no SDK, no headers to negotiate.
You do not need the app to create a project or generate that key: open the DevOmni web console in any browser, sign in by scanning the QR code with the app, and create the project there. It is also where you import API docs.
The key is a bearer credential: anyone holding this URL can post into your project. Treat it exactly like a password — see §5 for where to keep it.
3 · Request and response
A single POST with a JSON body. Nothing else is required.
The response looks like this:
What you send
| Field | Required | Notes |
|---|---|---|
title | yes | Plain text, up to 200 bytes. This is the push notification's headline, so keep it short and specific. |
content | yes | Markdown, up to 64 KB. Headings, bold, tables, code fences, links and - [ ] checklists all render. |
format | never | Legacy compatibility field. Sending it flattens your Markdown — see §5.1. |
What you get back
| Field | Meaning |
|---|---|
code | 0 means accepted. Anything else is a failure and message says why. |
data.messageId | The message's permanent ID. Keep it if you want to correlate with your own records. |
data.memberCount | How many project members the message was delivered to. |
data.pushCount | How many devices actually received a push notification. |
pushCount lower than memberCount is normal, not an error: some members have no registered device yet, or have declined notification permission. The message is stored either way and they will see it the next time they open the app — or immediately, if you have an email or bot channel configured.
4 · In your language
All five send the same request. Pick one, paste your push URL in, run it — you should get a notification before you finish reading this sentence.
5 · Four things that bite
5.1 · Never send a format field
This is the one that costs people an afternoon. format exists only so that very old clients keep working. Send "format":"markdown" and the server flattens your body into plain text before it ever reaches a phone — headings become bracketed labels, bold is stripped, and tables collapse into runs of separators. Omit the field and the same body renders properly.
| What you send | What the phone shows |
|---|---|
No format field (do this) | # Heading renders as a heading, **bold** as bold, | A | B | as a real table |
"format":"markdown" | 【Heading】, the bold gone, the table reduced to · 1:2 — text only |
There is no case in which a new integration should send this field. Just leave it out.
5.2 · Size limits
title is capped at 200 bytes and content at 64 KB. Both are counted in bytes, not characters, so a CJK title runs out roughly three times faster than an ASCII one. If you are forwarding a log, send the last few hundred lines and a link to the rest — a 64 KB wall of text is not something anyone reads on a phone anyway.
5.3 · Keep the key out of your repository
Anyone with the URL can post to the project, so it belongs in a CI secret or an environment variable — never in a committed file, and never in a client-side bundle. If it does leak, open Project Settings → Push Key → Rotate: the old URL stops working immediately, with no window in between. Revoke does the same thing permanently.
5.4 · People without the app still get alerted
Backend engineers, ops and managers should not have to install anything to hear about an outage. Project Settings → Alert Channels routes the same messages to email, a Feishu bot or a DingTalk bot. Each channel has a test button, so you can confirm it works the moment you configure it.
6 · Example: a build failure from GitHub Actions
Store the push URL as the repository secret DEVOMNI_PUSH_URL, then add one step guarded by if: failure(). Building the JSON with jq rather than string concatenation means a branch name with a quote in it cannot break the request. jq is preinstalled on GitHub-hosted runners.
The result on your phone is a heading, a small table of branch / commit / author, and a tappable link straight to the failing run. The same pattern drops into GitLab CI, Jenkins or a plain trap in a deploy script — nothing about it is GitHub-specific.
7 · Example: AI employees reporting in
An AI employee's defining property is that it works while you are not at the keyboard. That is exactly what makes a phone the right place for its output. Three moments are worth a push, and only three:
- It finished. What did it produce, and is it worth a look now?
- It is stuck. Execution failed, or it hit the edge of what it is allowed to do.
- It needs your approval. An action outside its current grant, waiting on you.
Without those three reaching you, you are back to watching a screen — which rather defeats the point of hiring one. The integration below is the ordinary push URL, so it works with any agent framework, any orchestration script, any cron-driven automation. We use it with KernelHub, where the Agent decides, a runtime such as Claude, Codex or Qwen does the thinking, and an Executor on your own Mac or Windows machine performs only actions you have authorised — but nothing here depends on it.
These snippets use requests (pip install requests). If you would rather not add a dependency, the standard-library version in §4 sends the identical request.
7.1 · It finished
Lead with the deliverable, not with "task complete". A table for the numbers, a link to the diff, and a checkbox you can tick off later.
7.2 · It is stuck, or it hit a boundary
Say what it wanted to do and which rule stopped it. A message that only says "failed" makes you open a laptop; this one usually does not.
7.3 · It needs your approval
Everything needed for the decision goes in the body: the exact action, the blast radius, whether it can be undone. If you have to open a laptop to decide, the notification has failed.
One thing that fits this workflow particularly well: what an agent pushes is not write-once. Open the report on your phone, edit the Markdown in place — add the conclusion you reached, tick off the checklist, delete the noise — and optionally notify the team again. You get one document that improves, not eleven near-identical copies in a list. Export it as .md when the work is done.
8 · Invite the team
Project Settings → Invite Members produces a QR code and a link. A teammate scans it with the app and is in the project; from that moment every push reaches their phone too, with no further configuration on your side. memberCount in the response goes up by one, which is the quickest way to confirm it worked.
For anyone who will not install the app, use Alert Channels instead (§5.4). And if you would rather work from a desktop, open the DevOmni web console (x.icloser.xyz/console) and scan the QR code with the app: the confirmation screen shows that machine's IP and browser, the code expires in two minutes, and it works exactly once. The console is also where you check endpoints and import an OpenAPI, Swagger or Postman document.
Next
That is the whole surface area. One URL, one POST, no SDK to keep up to date. If something does not behave the way this page describes, tell us — support reaches a person.
DevOmni