### Setup prompt for AI skill Source: https://m91-site.embarko.app/docs.html?doc=overview.md Copy this prompt and give it to your coding agent (Claude Code, Cursor, etc.). It instructs the agent to read the M91 guide, ask for your send link, and optionally install the m91-skill. The prompt ensures the send link is stored as a secret. ```text Set up M91 alerting in this project, so a real person gets woken when something breaks. Read https://m91.msg91.com/llms.txt and follow it. Nothing to install. Start with quickstart.md, then playbook.md, before writing any code. My send link — ask me for it. It is a live credential, so store it the way this project already stores its secrets, never in source. Faster, if your tools let you install one: npx skills add MSG91/m91-skill --skill m91-alerting -g ``` -------------------------------- ### Response for GET /api/v1/me - 200 success Source: https://m91-site.embarko.app/docs.html?doc=overview.md Example 200 response for GET /api/v1/me, showing the key's label and owner. ```json { "success": true, "data": { "label": "CRM backend", "owner": "…" } } ``` -------------------------------- ### Get key info with cURL Source: https://m91-site.embarko.app/docs.html?doc=overview.md Use this cURL command to call GET /api/v1/me. It requires an Authorization header with a Bearer token. A 200 response returns the key's label and owner; a 401 indicates a missing or revoked key. ```bash curl "https://m91.msg91.com/api/v1/me" \ -H "Authorization: Bearer $M91_API_KEY" ``` -------------------------------- ### Backend code examples for M91 alerts Source: https://m91-site.embarko.app/docs.html?doc=overview.md Use these snippets to send alerts from backend code, such as webhook handlers or failure branches. Read the send link from your secret store and post from the failure branch. The examples show Shell (curl), Node.js (fetch), and Python (requests) implementations. ```shell curl -X POST "$M91_SEND_LINK" -H "Content-Type: application/json" -d '{ "title": "Disk 94% on db-1", "severity": "HIGH", "customId": "disk-db-1", "responseOptions": [{"label": "I am on it", "effect": "SHARED"}, {"label": "Seen", "effect": "PERSONAL"}]}' ``` ```javascript await fetch(process.env.M91_SEND_LINK, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title: 'Payments webhook failing', description: `${count} failures since ${since.toISOString()}`, severity: 'CRITICAL', customId: 'payments-webhook-failing', responseOptions: [ { label: "I'll handle it", effect: 'SHARED' }, { label: 'Seen', effect: 'PERSONAL' }, ], }), }); ``` ```python requests.post(os.environ["M91_SEND_LINK"], json={ "title": "Nightly export did not run", "severity": "HIGH", "customId": "nightly-export", "responseOptions": [ {"label": "I'll handle it", "effect": "SHARED"}, {"label": "Seen", "effect": "PERSONAL"}, ], }) ``` -------------------------------- ### Add channel members - Response Source: https://m91-site.embarko.app/docs.html?doc=overview.md Example 200 response showing added, alreadyMembers, and invited arrays. ```json { "success": true, "data": { "added": [ "919812345678" ], "alreadyMembers": [ "919876543210" ], "invited": [ "919812345678" ] } } ``` -------------------------------- ### Get key info with JavaScript fetch Source: https://m91-site.embarko.app/docs.html?doc=overview.md Use this JavaScript snippet to call GET /api/v1/me using fetch. It requires an Authorization header with a Bearer token. A 200 response returns the key's label and owner; a 401 indicates a missing or revoked key. ```javascript const res = await fetch("https://m91.msg91.com/api/v1/me", { headers: { "Authorization": `Bearer ${process.env.M91_API_KEY}` }, }); const result = await res.json(); ``` -------------------------------- ### List channels - JavaScript Source: https://m91-site.embarko.app/docs.html?doc=overview.md Uses fetch to GET /api/v1/channels with Bearer token from environment variable. ```javascript const res = await fetch("https://m91.msg91.com/api/v1/channels", { headers: { "Authorization": `Bearer ${process.env.M91_API_KEY}` }, }); const result = await res.json(); ``` -------------------------------- ### Create channel response (201) Source: https://m91-site.embarko.app/docs.html?doc=overview.md Example response body for a successful channel creation. Contains the channel id, name, and sendLink. ```json { "success": true, "data": { "id": "6712ab…", "name": "Acme Corp on-call", "sendLink": "https://m91.msg91.com/s/sk_…" } } ``` -------------------------------- ### Get key info with Python requests Source: https://m91-site.embarko.app/docs.html?doc=overview.md Use this Python snippet to call GET /api/v1/me using the requests library. It requires an Authorization header with a Bearer token. A 200 response returns the key's label and owner; a 401 indicates a missing or revoked key. ```python import os, requests res = requests.get( "https://m91.msg91.com/api/v1/me", headers={"Authorization": f"Bearer {os.environ['M91_API_KEY']}"}, ) print(res.json()) ``` -------------------------------- ### Response for GET /api/v1/me - 401 unauthorized Source: https://m91-site.embarko.app/docs.html?doc=overview.md Example 401 response for GET /api/v1/me, indicating an invalid API key. ```json { "success": false, "error": { "code": "UNAUTHORIZED", "message": "Invalid API key" } } ``` -------------------------------- ### Send Alert API - JavaScript (fetch) Request Source: https://m91-site.embarko.app/docs.html?doc=overview.md Use this JavaScript example to send an alert using the fetch API. It uses the M91_API_KEY environment variable and sends the same JSON payload as the cURL example. The response is parsed as JSON and stored in the result variable. ```javascript const res = await fetch("https://m91.msg91.com/api/v1/alerts", { method: "POST", headers: { "Authorization": `Bearer ${process.env.M91_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ "phone": "919876543210", "title": "Payment 4821 failed", "description": "Card declined 3 times. Customer is waiting.", "severity": "HIGH", "customId": "payment-4821", "responseOptions": [ { "label": "I am on it", "effect": "SHARED" }, { "label": "Seen", "effect": "PERSONAL" } ] }), }); const result = await res.json(); ``` -------------------------------- ### Check send link success response Source: https://m91-site.embarko.app/docs.html?doc=overview.md Example 200 response when the link works. Includes channel and sender details. ```json { "success": true, "data": { "ok": true, "channel": { "id": "6712ab…", "name": "Payments", "visibility": "PRIVATE" }, "sender": { "id": "6712ac…", "label": "Grafana" } } } ``` -------------------------------- ### Check a send link with cURL Source: https://m91-site.embarko.app/docs.html?doc=overview.md Performs a GET request to the send link to confirm it is live. No headers are needed. A bare GET never raises an alert. ```bash curl "$M91_SEND_LINK" ``` -------------------------------- ### List channels - Response Source: https://m91-site.embarko.app/docs.html?doc=overview.md Example 200 response showing a success flag and an array of channel objects with id, name, and sendLink. ```json { "success": true, "data": [ { "_id": "6712ab…", "name": "Acme Corp on-call", "sendLink": "https://m91.msg91.com/s/sk_…" } ] } ``` -------------------------------- ### Python requests example to raise an alert Source: https://m91-site.embarko.app/docs.html?doc=overview.md Uses the requests library to POST to the M91_SEND_LINK environment variable with the same JSON payload. Prints the JSON response. Requires Content-Type: application/json. ```python import os, requests res = requests.post( os.environ["M91_SEND_LINK"], json={ "title": "Payment 4821 failed", "description": "Card declined 3 times. Customer is waiting.", "severity": "HIGH", "customId": "payment-4821", "responseOptions": [ { "label": "I am on it", "effect": "SHARED", }, { "label": "Seen", "effect": "PERSONAL", }, ], }, ) print(res.json()) ``` -------------------------------- ### GET /api/v1/me Source: https://m91-site.embarko.app/docs.html?doc=overview.md Retrieves the label and owner of the provided API key to verify its validity. ```APIDOC ## GET /api/v1/me ### Description Returns the key’s label and its owner. Use it to check a key is valid. ### Method GET ### Endpoint https://m91.msg91.com/api/v1/me ### Response #### Success Response (200) - **data** (object) - The key information containing label and owner. #### Response Example { "success": true, "data": { "label": "CRM backend", "owner": "…" } } ``` -------------------------------- ### Python requests example to close an alert Source: https://m91-site.embarko.app/docs.html?doc=overview.md Uses the requests library to POST to the /close endpoint with a customId. Prints the JSON response. ```python import os, requests res = requests.post( os.environ["M91_SEND_LINK"] + "/close", json={ "customId": "payment-4821", }, ) print(res.json()) ``` -------------------------------- ### Send an alert with JavaScript fetch Source: https://m91-site.embarko.app/docs.html?doc=overview.md Raises an alert via GET using URLSearchParams. Requires a title. Sets severity, respond, and customId. ```javascript const url = new URL(process.env.M91_SEND_LINK); url.searchParams.set("title", "Payment 4821 failed"); url.searchParams.set("severity", "HIGH"); url.searchParams.set("respond", "I am on it,Seen"); url.searchParams.set("customId", "payment-4821"); const res = await fetch(url); const result = await res.json(); ``` -------------------------------- ### Successful close response (200) Source: https://m91-site.embarko.app/docs.html?doc=overview.md Example response when the alert is successfully closed. Contains the alert ID and status RESOLVED. ```json { "success": true, "data": { "_id": "6703f1a2c8e4b90012ab34cd", "status": "RESOLVED" } } ``` -------------------------------- ### List channels - cURL Source: https://m91-site.embarko.app/docs.html?doc=overview.md Sends a GET request to /api/v1/channels with Bearer token authorization. Returns an array of channels. ```bash curl "https://m91.msg91.com/api/v1/channels" \ -H "Authorization: Bearer $M91_API_KEY" ``` -------------------------------- ### Send alert success response (201) Source: https://m91-site.embarko.app/docs.html?doc=overview.md Example 201 response when a new alert is raised. Includes alert details like status, severity, and delivery counts. ```json { "success": true, "data": { "_id": "6703f1a2c8e4b90012ab34cd", "channelId": "6712ab…", "channelName": "Payments", "customId": "payment-4821", "severity": "HIGH", "status": "OPEN", "isAutoClose": false, "createdAt": "2026-10-01T02:10:03.000Z", "delivered": 4, "unregistered": [], "noDevice": 1, "pending": 2 } } ``` -------------------------------- ### 404 error response for read alert Source: https://m91-site.embarko.app/docs.html?doc=overview.md Example error response when the alert is not found. The error code is NOT_FOUND and message is alert_not_found. ```json { "success": false, "error": { "code": "NOT_FOUND", "message": "alert_not_found" } } ``` -------------------------------- ### 200 response for read alert Source: https://m91-site.embarko.app/docs.html?doc=overview.md Example success response showing alert details, including responses and decision. Note that decision is null until a SHARED response is given. ```json { "success": true, "data": { "_id": "6703f1a2c8e4b90012ab34cd", "customId": "deploy-prod", "status": "RESOLVED", "severity": "HIGH", "title": "Approve deploy to prod?", "isAutoClose": false, "responseOptions": [ { "label": "Approve", "effect": "SHARED" }, { "label": "Reject", "effect": "SHARED" }, { "label": "Seen", "effect": "PERSONAL" } ], "attachments": [], "responses": [ { "label": "Approve", "effect": "SHARED", "optionIndex": 0, "at": "2026-10-01T03:14:22.000Z", "by": "…", "note": "checked the migration, go ahead", "attachments": [ { "id": "3a24…", "url": "https://…/photo.jpg", "name": "photo.jpg", "type": "image/jpeg", "size": 48213 } ] } ], "pending": 1, "decision": { "label": "Approve", "effect": "SHARED", "at": "2026-10-01T03:14:22.000Z", "by": "…" }, "resolvedAt": "2026-10-01T03:20:41.000Z" } } ``` -------------------------------- ### Send Alert API - Python (requests) Request Source: https://m91-site.embarko.app/docs.html?doc=overview.md Use this Python example to send an alert using the requests library. It reads the M91_API_KEY from the environment and sends the same JSON payload. The response is printed as JSON. ```python import os, requests res = requests.post( "https://m91.msg91.com/api/v1/alerts", headers={"Authorization": f"Bearer {os.environ['M91_API_KEY']}"}, json={ "phone": "919876543210", "title": "Payment 4821 failed", "description": "Card declined 3 times. Customer is waiting.", "severity": "HIGH", "customId": "payment-4821", "responseOptions": [ { "label": "I am on it", "effect": "SHARED", }, { "label": "Seen", "effect": "PERSONAL", }, ], }, ) print(res.json()) ``` -------------------------------- ### GET /s/{token} (Check Link) Source: https://m91-site.embarko.app/docs.html?doc=overview.md Confirms if a send link is live and retrieves the associated channel information without triggering an alert. ```APIDOC ## GET /s/{token} ### Description Confirms the link is live and names the channel it points to. It raises nothing and wakes nobody. ### Method GET ### Endpoint /s/{token} ### Parameters #### Path Parameters - **token** (string) - Required - The sk_ part of your send link. ### Response #### Success Response (200) - **success** (boolean) - Indicates if the request was successful. - **data** (object) - Contains channel and sender information. #### Response Example { "success": true, "data": { "ok": true, "channel": { "id": "6712ab…", "name": "Payments", "visibility": "PRIVATE" }, "sender": { "id": "6712ac…", "label": "Grafana" } } } ``` -------------------------------- ### Send an alert with cURL Source: https://m91-site.embarko.app/docs.html?doc=overview.md Raises an alert via GET with query parameters. Requires a title (5-2000 chars). Includes severity, respond, and customId parameters. ```bash curl -G "$M91_SEND_LINK" \ --data-urlencode "title=Payment 4821 failed" \ --data-urlencode "severity=HIGH" \ --data-urlencode "respond=I am on it,Seen" \ --data-urlencode "customId=payment-4821" ``` -------------------------------- ### Send an alert with Python requests Source: https://m91-site.embarko.app/docs.html?doc=overview.md Raises an alert via GET using the requests library with params. Requires a title. Sets severity, respond, and customId. ```python import os, requests res = requests.get( os.environ["M91_SEND_LINK"], params={ "title": "Payment 4821 failed", "severity": "HIGH", "respond": "I am on it,Seen", "customId": "payment-4821", }, ) print(res.json()) ``` -------------------------------- ### Check send link (curl) Source: https://m91-site.embarko.app/docs.html?doc=overview.md Runs a GET request to the send link. Should return the channel name and raise no alert. If you see a '?title=...' it sends a real alert. ```bash # Raises nothing. Nobody is alerted. curl -sS "$M91_SEND_LINK" # You should see: the name of your channel ``` -------------------------------- ### GET /s/{token} (Send Alert) Source: https://m91-site.embarko.app/docs.html?doc=overview.md Raises an alert by providing a title and optional parameters via query string. This is designed for use in webhook fields or simple URL-based triggers. ```APIDOC ## GET /s/{token} ### Description Raises an alert from a plain URL. Use it wherever you can only paste a link. ### Method GET ### Endpoint /s/{token} ### Parameters #### Path Parameters - **token** (string) - Required - The sk_ part of your send link. #### Query Parameters - **title** (string) - Required - 5–2000 characters. Without it nothing is raised. - **description** (string) - Optional - Up to 2000 characters. - **severity** (enum) - Optional - LOW, MEDIUM, HIGH or CRITICAL. Default MEDIUM. - **respond** (string) - Optional - Comma-separated answer labels, up to 6. - **isAutoClose** (boolean) - Optional - true, false, 1, 0, yes, no, on, off. Default false. - **customId** (string) - Optional - Up to 200 characters. ### Response #### Success Response (201) - **data** (object) - Details of the newly raised alert. #### Success Response (200) - **data** (object) - Details of the suppressed repeat alert. #### Response Example { "success": true, "data": { "_id": "6703f1a2c8e4b90012ab34cd", "channelId": "6712ab…", "channelName": "Payments", "customId": "payment-4821", "severity": "HIGH", "status": "OPEN" } } ``` -------------------------------- ### POST /s/{token} Source: https://m91-site.embarko.app/docs.html?doc=overview.md Raises an alert with the full payload. This is the main endpoint: one URL, no SDK, no headers except Content-Type. Authorization is done by sending the link token in the path. ```APIDOC ## POST /s/{token} ### Description Raises an alert with the full payload. This is the main endpoint: one URL, no SDK, no headers except `Content-Type`. ### Method POST ### Endpoint /s/{token} ### Authorization Send link token in the path. No headers needed. ### Parameters #### Path Parameters - **token** (string) - Required - The `sk_…` part of your send link. Use the whole link as one string. #### Request Body - **title** (string) - Required - 5–2000 characters. The line someone reads on a lock screen. `summary` is accepted as an alias. - **description** (string) - Optional - Up to 2000 characters. What happened and what is needed to act. - **severity** (enum) - Optional - `LOW`, `MEDIUM`, `HIGH` or `CRITICAL`. Default `MEDIUM`. See Severity. - **responseOptions** (array) - Optional - Up to 20 answer buttons. See Response options. Omitted means nobody can answer and nothing can close the alert. - **other** (boolean) - Optional - Default `true`. Adds a **Something else** reply where the person types an answer. - **attachments** (string[]) - Optional - `https` links to files shown with the alert. They must open without a login. See Attachments. - **callbackUrl** (string) - Optional - An `https` URL M91 POSTs to when someone answers and when the alert ends. See Callbacks. - **isAutoClose** (boolean) - Optional - Default `false`. Whether the alert closes itself. See How an alert ends. - **customId** (string) - Optional - Up to 200 characters. Your own id for the condition. Collapses repeats. See Repeats. ### Response option object - **label** (string) - Required - Up to 24 characters. Written as the answer, in the first person. Labels must differ. - **effect** (enum) - Optional - `SHARED` (I take responsibility) or `PERSONAL` (I do not). Default `SHARED`. - **required** (string[]) - Optional - What the person must add to answer: `"note"`, `"attachment"`, or both. - **optional** (string[]) - Optional - What the person may add: `"note"`, `"attachment"`, or both. Not the same field as in `required`. ``` -------------------------------- ### List channels - Python Source: https://m91-site.embarko.app/docs.html?doc=overview.md Uses requests.get to fetch channels with Bearer token from environment variable. ```python import os, requests res = requests.get( "https://m91.msg91.com/api/v1/channels", headers={"Authorization": f"Bearer {os.environ['M91_API_KEY']}"}, ) print(res.json()) ``` -------------------------------- ### LangChain: M91ApprovalTool usage Source: https://m91-site.embarko.app/docs.html?doc=overview.md Creates an M91ApprovalTool that the agent calls to request approval. It blocks until a person answers, with configurable labels and timeout. The prompt instructs the agent to call m91_request_approval and wait for the exact string 'approved'. ```python from m91.integrations.langchain import M91ApprovalTool approval = M91ApprovalTool( send_link=os.environ["M91_SEND_LINK"], default_approve_label="Refund", default_reject_label="Hold", default_timeout_seconds=600, ) agent = create_react_agent( init_chat_model("claude-sonnet-5"), [approval, *your_tools], prompt=( "Before any refund, payment, deletion or deploy, call " "m91_request_approval and wait. Only the exact string 'approved' " "lets you continue." ), ) ``` -------------------------------- ### Callback Event Payload Source: https://m91-site.embarko.app/docs.html?doc=overview.md Example of the JSON payload sent to the callbackUrl when an alert is answered or closed. ```APIDOC ## Callback Event Payload ### Description M91 sends a message to the `callbackUrl` when someone answers an alert (`answered`) and when the alert ends (`closed`). The payload includes event details, alert information, and response data. ### Event Types - `answered` - Sent once per person when they answer. - `closed` - Sent when the alert ends, providing the final picture: `reason`, all `responses`, and who is still `pending`. ### Payload Example ```json { "event": "answered", "at": "2026-10-01T12:14:09.535Z", "alert": { "id": "…", "customId": "deploy-4821", "title": "…" }, "response": { "label": "Approve", "effect": "SHARED", "by": { "id": "…", "name": "Aryan" }, "at": "…", "note": "go ahead", "attachments": [] }, "idempotencyKey": "--" } ``` ### Fields - `event` (string) - The event type: `answered` or `closed`. - `at` (string) - Timestamp of the event. - `alert` (object) - Contains `id`, `customId`, and `title` of the alert. - `response` (object) - Contains `label`, `effect`, `by`, `at`, `note`, and `attachments`. - `idempotencyKey` (string) - Unique key to ignore duplicate messages. ### Notes - The same message can arrive twice; use `idempotencyKey` to ignore repeats. - Check the signature: HMAC-SHA256 of `"."` using the secret returned when the alert was created. Use the raw body and reject old timestamps. - Reply with `2xx` quickly. `note` and `attachments` are always present (`null` or `[]` when empty). ``` -------------------------------- ### Check send link unauthorized response Source: https://m91-site.embarko.app/docs.html?doc=overview.md Example 401 response when the token is wrong or revoked. ```json { "success": false, "error": { "code": "UNAUTHORIZED", "message": "Invalid sender token" } } ``` -------------------------------- ### Vercel AI SDK: createM91ApprovalTool usage Source: https://m91-site.embarko.app/docs.html?doc=overview.md Creates an approval tool inside generateText using createM91ApprovalTool. Configures severity, labels, and timeout. The tool is named m91_request_approval and is used in the prompt. ```javascript import { M91Client } from 'm91'; import { createM91ApprovalTool } from 'm91/integrations/vercel-ai'; const m91 = new M91Client(process.env.M91_SEND_LINK!); const { text } = await generateText({ model: openai('gpt-4o'), tools: { m91_request_approval: await createM91ApprovalTool(m91, { severity: 'HIGH', approveLabel: 'Refund', rejectLabel: 'Hold', timeoutSeconds: 600, }), }, stopWhen: stepCountIs(10), prompt: 'Refund order ORD-40122 if it checks out.', }); ``` -------------------------------- ### GET /api/v1/channels Source: https://m91-site.embarko.app/docs.html?doc=overview.md Returns every channel you can send to. Not paginated. Requires an Authorization header with a Bearer token. ```APIDOC ## GET /api/v1/channels ### Description Returns every channel you can send to. Not paginated. ### Method GET ### Endpoint /api/v1/channels ### Parameters #### Headers - **Authorization** (string) - Required - Bearer token for authentication. ### Request Example ``` curl "https://m91.msg91.com/api/v1/channels" \ -H "Authorization: Bearer $M91_API_KEY" ``` ### Response #### Success Response (200) - **success** (boolean) - Indicates if the request was successful. - **data** (array) - An array of channel objects. - **_id** (string) - The channel ID. - **name** (string) - The channel name. - **sendLink** (string) - The link to send messages to the channel. #### Response Example ``` { "success": true, "data": [ { "_id": "6712ab…", "name": "Acme Corp on-call", "sendLink": "https://m91.msg91.com/s/sk_…" } ] } ``` ``` -------------------------------- ### Create channel with cURL Source: https://m91-site.embarko.app/docs.html?doc=overview.md Sends a POST request to create a channel. Requires an API key in the Authorization header. The body includes a name and optionally alertOwner set to false to avoid paging the owner. ```bash curl -X POST "https://m91.msg91.com/api/v1/channels" \ -H "Authorization: Bearer $M91_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Acme Corp on-call", "alertOwner": false }' ``` -------------------------------- ### Create channel with JavaScript fetch Source: https://m91-site.embarko.app/docs.html?doc=overview.md Uses fetch to POST to the channels endpoint. Reads the API key from the environment variable M91_API_KEY. The response is parsed as JSON. ```javascript const res = await fetch("https://m91.msg91.com/api/v1/channels", { method: "POST", headers: { "Authorization": `Bearer ${process.env.M91_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ "name": "Acme Corp on-call", "alertOwner": false }), }); const result = await res.json(); ``` -------------------------------- ### cURL request to read an alert Source: https://m91-site.embarko.app/docs.html?doc=overview.md Uses the M91_SEND_LINK environment variable and the alert customId 'deploy-prod' to fetch alert details. No headers are needed. ```bash curl "$M91_SEND_LINK/alerts/deploy-prod" ``` -------------------------------- ### Send alert validation error response (400) Source: https://m91-site.embarko.app/docs.html?doc=overview.md Example 400 response when a field is invalid, such as an invalid severity value. ```json { "success": false, "error": { "code": "VALIDATION_ERROR", "message": "severity must be one of: LOW, MEDIUM, HIGH, CRITICAL", "details": { "fields": [ { "field": "severity", "message": "severity must be one of: LOW, MEDIUM, HIGH, CRITICAL" } ] } } } ``` -------------------------------- ### Check a send link with JavaScript fetch Source: https://m91-site.embarko.app/docs.html?doc=overview.md Uses fetch to GET the send link and parse the JSON response. No headers are needed. ```javascript const res = await fetch(process.env.M91_SEND_LINK); const result = await res.json(); ``` -------------------------------- ### Python requests to read an alert Source: https://m91-site.embarko.app/docs.html?doc=overview.md Uses the requests library with the M91_SEND_LINK environment variable and the alert customId 'deploy-prod'. Prints the JSON response. ```python import os, requests res = requests.get(os.environ["M91_SEND_LINK"] + "/alerts/deploy-prod") print(res.json()) ``` -------------------------------- ### Send alert duplicate suppressed response (200) Source: https://m91-site.embarko.app/docs.html?doc=overview.md Example 200 response when a repeat is suppressed because an alert with the same customId is already open. ```json { "success": true, "data": { "_id": "6703f1a2c8e4b90012ab34cd", "customId": "payment-4821", "status": "OPEN", "delivered": 0, "deduplicated": true, "message": "An alert with customId \"payment-4821\" is already open, so nobody was alerted again." } } ``` -------------------------------- ### Check a send link with Python requests Source: https://m91-site.embarko.app/docs.html?doc=overview.md Uses the requests library to GET the send link and print the JSON response. No headers are needed. ```python import os, requests res = requests.get(os.environ["M91_SEND_LINK"]) print(res.json()) ``` -------------------------------- ### Callback event JSON example Source: https://m91-site.embarko.app/docs.html?doc=overview.md Shows the structure of a callback message sent by M91 when someone answers an alert. The event includes the response label, effect, responder, and an idempotency key to handle duplicate messages. ```json { "event": "answered", "at": "2026-10-01T12:14:09.535Z", "alert": { "id": "…", "customId": "deploy-4821", "title": "…" }, "response": { "label": "Approve", "effect": "SHARED", "by": { "id": "…", "name": "Aryan" }, "at": "…", "note": "go ahead", "attachments": [] }, "idempotencyKey": "--" } ```