API
Publish on Mishkan from outside the app.
Updated 2026-08-19
A Mishkan API key lets a studio, a script or a server read the feed and publish on it in your name. It is a second door into your own identity — not a service account: a key obeys every rule you obey, from your verification level to the Mem a post costs and the people you have blocked.
Everything below is served from https://api.mishkan.tech. Responses are JSON; errors carry an HTTP status and a single "error" string.
Getting a key
Keys are minted inside the app: Profile → API keys → +. Creating one requires level 4 (formal verification), the same bar as a business sub-account — a key acts in your name outside the app, so we hand one out only to an account an admin has identified. Listing and revoking work at any level.
- Name — for your own bookkeeping («Studio Midbar»).
- Publish as — your personal account, or one of your business sub-accounts. A key pinned to a business can publish ONLY as that business; naming another identity in a request is refused.
- Allow publishing — off, the key can only read.
The secret (msk_live_…) is shown once. The server keeps only its SHA-256, so it cannot be recovered — a lost key is revoked and replaced. You may hold up to 20 live keys, each with an optional expiry of up to ten years.
Authentication
Send the key as a bearer token. The X-API-Key header is accepted too, for clients that reserve Authorization for themselves.
curl https://api.mishkan.tech/api/users/me \
-H "Authorization: Bearer $MISHKAN_KEY"An unknown, revoked or expired key all answer the same 401 — deliberately, so probing tells an attacker nothing.
Scopes
A key carries posts:read, posts:write, or both. Everything else — messages, Mem, verification, administration, and key management itself — stays reserved to a signed-in session. A leaked key can neither mint another one, nor read your conversations, nor spend your balance.
Endpoints
| Route | Scope | Level required |
|---|---|---|
| GET /api/users/me | posts:read | — |
| GET /api/businesses | posts:read | — |
| GET /api/cities | posts:read | — |
| GET /api/posts/mine | posts:read | — |
| GET /api/feed?kind=visual|text | posts:read | 1 (verified email) |
| GET /api/posts/{id} | posts:read | 1 (verified email) |
| POST /api/posts | posts:write | 3 to publish on the public feed |
| POST /api/posts/visual | posts:write | 3 to publish on the public feed |
| POST /api/posts/youtube | posts:write | 3 to publish on the public feed |
| DELETE /api/posts/{id} | posts:write | author or admin |
Publishing into a group's own feed (conversation_id) replaces the level gate with membership and the group's posting policy, and pays the group's own price to its owner.
curl "https://api.mishkan.tech/api/feed?kind=text&limit=20" \
-H "Authorization: Bearer $MISHKAN_KEY"Publishing
A post on the public feed costs 2 מ and requires mezuzah verification (level 3). The charge is refunded if the post fails to save.
POST /api/posts publishes text and links, as JSON:
| Field | Meaning |
|---|---|
| content | up to 4000 characters |
| link_url | optional absolute http(s) URL, unfurled and shown as a card |
| language | fr | en | he — the feed's language filter |
| city | city slug — the feed's city filter |
| business_id | optional; must match the key's identity when it is pinned |
| conversation_id | optional; publishes into that group's feed instead of the public one |
curl -X POST https://api.mishkan.tech/api/posts \
-H "Authorization: Bearer $MISHKAN_KEY" \
-H "Content-Type: application/json" \
-d '{"content":"Chag sameach","language":"en"}'POST /api/posts/visual publishes a reel or a carousel as multipart/form-data: 1 to 10 media files, in form order, up to 30 MB for the whole request. The caption goes in content (1000 characters), and the same optional fields apply.
curl -X POST https://api.mishkan.tech/api/posts/visual \
-H "Authorization: Bearer $MISHKAN_KEY" \
-F "content=Kabbalat Shabbat, 18:30" \
-F "media=@reel.mp4;type=video/mp4"POST /api/posts/youtube publishes a reel backed by a YouTube video. url is the video; link_url is a separate “learn more” button, so fill both when you want the two.
curl -X POST https://api.mishkan.tech/api/posts/youtube \
-H "Authorization: Bearer $MISHKAN_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://youtu.be/abc123","link_url":"https://example.org"}'Limits
| Limit | Value |
|---|---|
| Reads | ~60 per minute, burst 30 |
| Writes | ~20 per minute, burst 5 |
| Per IP address | ~300 per minute, burst 60 |
| Request size | 30 MB (App Engine caps at 32 MB) |
| Media per post | 10 |
| Live keys per account | 20 |
Errors
{ "error": "missing the posts:write scope" }| Status | What it means |
|---|---|
| 401 invalid API key | unknown, revoked or expired — the three are answered alike |
| 403 missing the posts:write scope | the key is read-only |
| 403 verification level 3 required | the account is not mezuzah-verified, which the public feed requires |
| 403 can only publish as the business account it is bound to | the request named an identity other than the key's |
| 402 | not enough מ to pay for the post |
| 429 | rate limited — back off and retry |
Revoking
Swipe the key (iOS) or tap the bin (Android), or call DELETE /api/api-keys/{id} with a session token. The effect is immediate. Posts already published stay online: revoking a credential is not withdrawing what was published with it.