# SelfPing, complete reference

> SelfPing is a simple API that sends a text message to your own phone from any app, website, server, or API. One endpoint, one API key, no Twilio account required.

This file is the whole integration in one fetch. A key is bound to the phone number that created it. There is no recipient field anywhere in the API, so SelfPing cannot message anyone except the account owner, and a leaked key cannot text third parties.

## Getting a key (the one human step)

The only thing an agent cannot do is create the account, because ownership of the phone number is proven with a one-time passcode. Send the user to https://www.selfping.com/login, have them sign in with their phone number, and generate a key at https://www.selfping.com/dashboard. Everything after that is automatable.

Store the key as `SELFPING_API_KEY` in the environment, not in committed files. Keys look like `sp_...` or a short token, and the dashboard shows the current one.

## Send a text

```bash
curl -X POST https://www.selfping.com/api/sms \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SELFPING_API_KEY" \
  -d '{"message": "Deploy finished. 0 errors."}'
```

The only body field is `message`, a required string. Every send costs one credit regardless of length. Keep it under 160 characters anyway, because short texts read best on a lock screen.

Responses:

- `200` body `SMS sent successfully`. The text was handed to the carrier; final delivery state appears on the user's dashboard.
- `400` when `message` is missing or the key has no phone attached.
- `401` when the Authorization header is missing.
- `403` when the key is invalid, revoked, or out of credits.
- `502` with a plain-language reason when the text could not be handed off.

### Node

```bash
npm install selfping
```

```js
import sendPing from "selfping";

await sendPing("New signup: jane@example.com");
```

Zero dependencies, reads `SELFPING_API_KEY` from the environment, throws `SelfPingError` with the reason on failure. Or without the package:

```js
await fetch("https://www.selfping.com/api/sms", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.SELFPING_API_KEY}`,
  },
  body: JSON.stringify({ message: "New signup: jane@example.com" }),
});
```

### Python

```python
import os, requests

requests.post(
    "https://www.selfping.com/api/sms",
    headers={"Authorization": f"Bearer {os.environ['SELFPING_API_KEY']}"},
    json={"message": "Nightly backup finished in 4m12s"},
)
```

## MCP server

Streamable HTTP at https://www.selfping.com/api/mcp, exposing one tool, `send_ping({message})`. Authenticate with the same bearer key.

Claude Code:

```bash
claude mcp add --transport http selfping https://www.selfping.com/api/mcp \
  --header "Authorization: Bearer sp_your_api_key"
```

Any MCP client:

```json
{
  "mcpServers": {
    "selfping": {
      "type": "http",
      "url": "https://www.selfping.com/api/mcp",
      "headers": {
        "Authorization": "Bearer sp_your_api_key"
      }
    }
  }
}
```

Connecting the server gives an agent the ability to text its user. It does not give it the habit. Publish this in the project's agent rules file (CLAUDE.md or equivalent) so the agent knows when to reach for it:

```markdown
## Notifications

Text me via SelfPing (`send_ping`) when:

- a run takes more than a couple of minutes and I am not watching
- anything fails or needs a decision from me
- I explicitly ask to be told when something finishes

Do not text me for a routine pass with no surprises, or while I am clearly
sitting here reading your output.

Keep it under 160 characters so it reads clean on a lock screen. Lead with the outcome,
include the numbers, and say plainly whether anything needs me.
```

## Completion hooks (fire on every run)

For texting the user every time an agent run ends, whether the agent thought to or not.

Claude Code, in `.claude/settings.json` (restart the agent after pasting; hook config is read once at startup, and export `SELFPING_API_KEY` before launching):

```json
{
  "hooks": {
    "Notification": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "http",
            "url": "https://www.selfping.com/api/hooks/claude-code",
            "headers": {
              "Authorization": "Bearer ${SELFPING_API_KEY}"
            },
            "allowedEnvVars": ["SELFPING_API_KEY"]
          }
        ]
      }
    ]
  }
}
```

Cursor and Codex run a one-line curl from their hook config. SelfPing reads the payload they already send, so there is no script to install:

```bash
curl -sS -X POST https://www.selfping.com/api/hooks/cursor \
  -H "Authorization: Bearer $SELFPING_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @-
```

Swap `cursor` for `codex` in the URL for Codex. Add `?label=my-project` to tell projects apart. Hook endpoints always answer 200 with `{"continue": true}`, even on a missing or dead key, so SelfPing can never break an agent session; when nothing was sent, the reason comes back in `systemMessage`.

## When to send (guidance worth adopting)

Send when a run takes more than a couple of minutes unattended, when anything fails or needs a decision, or when the user asked to be told. Do not send for a routine pass with no surprises. Lead with the outcome, include the numbers that matter, and say plainly whether anything needs the user. A text that fits in 160 characters reads best on a lock screen.

## Pricing

| Plan | Price | Texts per month |
|---|---|---|
| Free | $0 | 10 |
| Hobby | $5/mo or $50/yr | 100 |
| Business | $19/mo or $190/yr | 400 |

One send costs one credit regardless of message length. Credits refill on the 1st. No credit card to start. No overage billing: sends stop at zero until refill or upgrade.

## Everything else

- OpenAPI: https://www.selfping.com/openapi.json
- Credential setup for agents: https://www.selfping.com/auth.md
- MCP server card: https://www.selfping.com/.well-known/mcp/server-card.json
- Agent skill: https://www.selfping.com/.well-known/agent-skills/selfping-send-text/SKILL.md
- Human docs: https://www.selfping.com/agents
- Every content page answers to `Accept: text/markdown` and to a `.md` suffix, e.g. https://www.selfping.com/pricing.md
