MishkanMishkan

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

RouteScopeLevel required
GET /api/users/meposts:read
GET /api/businessesposts:read
GET /api/citiesposts:read
GET /api/posts/mineposts:read
GET /api/feed?kind=visual|textposts:read1 (verified email)
GET /api/posts/{id}posts:read1 (verified email)
POST /api/postsposts:write3 to publish on the public feed
POST /api/posts/visualposts:write3 to publish on the public feed
POST /api/posts/youtubeposts:write3 to publish on the public feed
DELETE /api/posts/{id}posts:writeauthor 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:

FieldMeaning
contentup to 4000 characters
link_urloptional absolute http(s) URL, unfurled and shown as a card
languagefr | en | he — the feed's language filter
citycity slug — the feed's city filter
business_idoptional; must match the key's identity when it is pinned
conversation_idoptional; 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

LimitValue
Reads~60 per minute, burst 30
Writes~20 per minute, burst 5
Per IP address~300 per minute, burst 60
Request size30 MB (App Engine caps at 32 MB)
Media per post10
Live keys per account20

Errors

{ "error": "missing the posts:write scope" }
StatusWhat it means
401 invalid API keyunknown, revoked or expired — the three are answered alike
403 missing the posts:write scopethe key is read-only
403 verification level 3 requiredthe account is not mezuzah-verified, which the public feed requires
403 can only publish as the business account it is bound tothe request named an identity other than the key's
402not enough מ to pay for the post
429rate 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.