A tool you connect can read your goals, your buddies' shared goals and your conversations. Depending on its permissions, it can also change or delete goals and message your buddy as you. It can't see photos.
Your conversations include your buddies' comments and check-ins. Your buddy isn't told that you've connected a tool. A token never sees more than you do, because every call runs under your own account with the same rules as the app.
A token can't make or revoke tokens, end a pair, remove a buddy, send or accept an invite, add photos or delete your account. Those need you, in the app. The server refuses them even if a request gets that far. A stolen token can't cut you off from your buddy or bring a stranger in.
A token is bp_ followed by 32 random bytes and a 6-character checksum. The checksum lets a scanner tell a real token from a typo without asking us. We store only its SHA-256 hash, so we can't show it to you again, and neither can anyone who reads our database.
You pick an expiry when you make one: 30 days, 90 days, 1 year or never. You can revoke one at any time in the app, and it stops working on its next call. Deleting your account revokes all of them.
Put your token in an environment variable or a secret manager, and never in a web page. The API sends no CORS headers, so a browser page can't call it. If a token leaks, revoke it in the app and make a new one. Revoking doesn't stop your webhooks, so check them on the same screen and delete any you don't recognize.
Every delivery is signed with your webhook's secret, so you can check that it came from BuddyPad and hasn't changed. See Events and webhooks.
BuddyPad refuses to deliver to an internal address: private, loopback, link-local, carrier-grade NAT, the cloud metadata address, or an IPv6, IPv4-mapped or NAT64 form of any of those. It checks every address the name resolves to before every delivery, follows no redirects and gives up after 10 seconds.
There's one limit. Our delivery code runs on Cloudflare Workers, which can't pin the address we checked. fetch resolves the name again, so a name that answers with a public address to our check and a private one a moment later would get past it. We tested this on a deployed Worker, and Cloudflare refused every private address we tried. That test used bare addresses and one name that points at loopback, which isn't every case. If you run a receiver inside a private network, don't point a public name at it.
We log request counts, status codes and error codes. We never log a request body, a response body, a token or a secret.
Deleting your account in the app revokes your tokens and deletes your webhooks and their secrets. The webhook delivery log and the events feed each keep 30 days, and your rows go with your account.
Email buddypad@builtbymo.dev.