Mumro API docs.mumro.io
Endpointy Przyklady

Public API v1

Dokumentacja API Mumro

Stabilny kontrakt publicznego API dla kolejkowania publikacji, uploadow mediow i zarzadzania zadaniami publikacji. Jeden request moze obsluzyc wiele kont i platform, ze wspolna trescia, nadpisaniami per target oraz natywnymi opcjami platform przez platform_options.

POST /api/publications
{
  "publication_type": "post",
  "targets": [42, 43],
  "common_content": {
    "caption": "Hello from the API",
    "media_asset_ids": [1234]
  },
  "schedule_mode": "now"
}

Pierwszy request

Quick start

API akceptuje token Bearer. Najprostszy przeplyw to wygenerowanie klucza API w aplikacji, wybranie przykladu pasujacego do procesu i wyslanie requestu z naglowkiem autoryzacji.

1

Wygeneruj klucz

W aplikacji przejdz do Settings -> API keys i utworz nowy klucz smm_....

2

Dobierz przeplyw

Skorzystaj z sekcji Przyklady: post, reel, story, film albo kolejka zadan.

3

Wyslij request

Dodaj Authorization, a dla tworzenia publikacji takze Idempotency-Key.

Nigdy nie commituj klucza API.

Trzymaj klucz w menedzerze sekretow lub zmiennej srodowiskowej. Jesli klucz wycieknie, odwolaj go i wygeneruj nowy.

Authentication

Autoryzacja

API przyjmuje token Bearer: klucz API smm_... wygenerowany w UI albo JWT z sesji SPA. Klucze API sa przypisane do organizacji, dlatego naglowek X-Organization-Id jest dla nich opcjonalny.

Uzycie klucza

Request header
Authorization: Bearer smm_REPLACE_ME

Klucz jest widoczny tylko raz przy tworzeniu. Jesli go utracisz, odwolaj stary klucz i wygeneruj nowy.

Kiedy wysylac X-Organization-Id

Principal Naglowek wymagany?
API key smm_... Nie, klucze sa org-scoped.
JWT, uzytkownik jednej organizacji Nie, organizacja jest wywnioskowana.
JWT, uzytkownik z co najmniej 2 aktywnymi czlonkostwami Tak, ustaw X-Organization-Id.

Brak naglowka dla multi-org JWT zwraca 401 organization_required.

Idempotency

Idempotencja

Aby bezpiecznie ponawiac requesty po awariach sieci, dolacz naglowek Idempotency-Key. Wartoscia moze byc UUID v4 albo string o dlugosci 16-128 znakow. Okno idempotencji trwa 24 godziny od pierwszego sukcesu, a serwer odsyla wartosc w Idempotency-Key-Echo.

Idempotency header
Idempotency-Key: f47ac10b-58cc-4372-a567-0e02b2c3d479
Sytuacja Odpowiedz
Identyczny payload w ciagu 24 godzin 200 z cache'owanym body pierwszego sukcesu.
Inny request z tym samym kluczem jest nadal przetwarzany 409 z error_code: idempotency_in_progress.
Ten sam klucz, inny payload 422 z error_code: idempotency_key_conflict.
Niepoprawny format klucza 400 z error_code: idempotency_key_invalid.

Rate limits

Limity

Limity sa naliczane per klucz API albo per JWT subject z uzyciem sliding window. Udane odpowiedzi zawieraja naglowki X-RateLimit-Limit, X-RateLimit-Remaining i X-RateLimit-Reset. Po przekroczeniu budzetu API zwraca 429 z error_code: rate_limited oraz Retry-After.

60 / 60s POST /api/publications
30 / 60s POST /api/uploads*
300 / 60s Inne /api/*
Przykladowe response headers
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
X-RateLimit-Reset: 1718524800
Retry-After: 17

Errors

Bledy

Kazda odpowiedz >= 400 zwraca kanoniczna koperte. request_id pochodzi z naglowka X-Request-Id, a lista error_code jest zamknieta i zdefiniowana w app.services._errors.ERROR_CODES.

ErrorEnvelope
{
  "error_code": "validation_error",
  "error_message": "Walidacja zadania nie powiodla sie",
  "request_id": "1a2b3c...",
  "details": null
}
error_code HTTP Opis
missing_bearer_token401Brak naglowka Authorization albo cookie.
invalid_api_key401Klucz API jest odwolany albo nieznany.
organization_required401Multi-org JWT musi ustawic X-Organization-Id.
organization_forbidden403Principal nie nalezy do wybranej organizacji.
tenant_forbidden403Operacja wymaga wyzszej roli OrgRole.
validation_error422Walidacja body albo query w Pydantic nie powiodla sie.
idempotency_in_progress409Ten sam Idempotency-Key jest nadal przetwarzany.
idempotency_key_conflict422Klucz idempotencji zostal uzyty z innym payloadem.
idempotency_key_invalid400Niepoprawny format Idempotency-Key.
target_not_found404Jeden z targetow nie nalezy do tenanta.
all_targets_failed422Wszystkie targety publikacji zakonczyly sie bledem.
platform_does_not_support_type422Platforma konta nie obsluguje danego publication_type.
schedule_required_for_custom422schedule_mode='custom' wymaga terminu publikacji.
invalid_status_transition409Cancel albo retry zostal wywolany dla niezgodnego statusu joba.
scheduled_at_in_past400PATCH scheduled_at wskazuje przeszlosc.
media_too_large413Upload przekracza limit rozmiaru dla typu mediow.
rate_limited429Przekroczono limit per klucz.
account_not_found404account_id nie nalezy do wybranego tenanta.

Reference

Endpointy

Wszystkie endpointy wymagaja naglowka Authorization: Bearer <token>. Endpointy oznaczone jako idempotent honoruja naglowek Idempotency-Key.

Method URL Role Idempotent Opis Statusy
POST /api/publications member+ Tak Tworzy unified publication i rozbija ja na jeden PublishJob per target. Body: PublicationCreate. 200, 201, 207, 401, 403, 404, 409, 422, 429
GET /api/publications/capabilities viewer+ - Macierz platforma x typ, limity caption, limity mediow i opcje story. 200, 401, 403, 429
GET /api/publications/preview-schedule viewer+ - Najwczesniejszy wolny slot dla account_id i content_type. 200, 401, 403, 404, 429
GET /api/publish-jobs viewer+ - Stronicowana kolejka i historia. Filtry: status[], platform[], account_id[], daty, sort, limit, offset. 200, 400, 401, 403, 429
GET /api/publish-jobs/{id} viewer+ - Pojedyncza projekcja joba PublishJobRead. 200, 401, 403, 404, 429
POST /api/publish-jobs/{id}/cancel member+ - Przenosi queued albo scheduled job do statusu canceled. 200, 401, 403, 404, 409, 429
POST /api/publish-jobs/{id}/retry member+ - Przenosi failed albo canceled job do kolejki. 200, 401, 403, 404, 409, 429
PATCH /api/publish-jobs/{id} member+ - Edytuje caption, title albo termin queued lub scheduled joba. 200, 400, 401, 403, 404, 409, 429
POST /api/publish-jobs/bulk/cancel member+ - Anuluje do 500 jobow. Body: { ids: number[] }. 200, 401, 403, 422, 429
POST /api/publish-jobs/bulk/retry member+ - Ponawia do 500 jobow. Body: { ids: number[] }. 200, 401, 403, 422, 429
POST /api/uploads/init member+ - Inicjuje presigned upload, single PUT albo multipart. 201, 400, 401, 403, 413, 429
POST /api/uploads/{public_id}/complete member+ - Finalizuje presigned upload i oznacza MediaAsset jako ready. 200, 400, 401, 403, 404, 429
POST /api/uploads/{public_id}/abort member+ - Anuluje upload w toku i oznacza asset jako failed. 200, 401, 403, 404, 429
POST /api/uploads/direct member+ - Server-streamed upload multipart/form-data do 100 MB. 201, 400, 401, 403, 413, 429
POST /api/uploads/from-url member+ - Server-side fetch i upload z zewnetrznego URL z whitelisty. 200, 401, 403, 422, 429, 502
GET /api/api-keys member+ - Lista aktywnych kluczy API dla aktualnej organizacji. 200, 401, 403, 429
POST /api/api-keys member+ - Generuje nowy klucz API. Token plaintext wraca tylko raz. 201, 401, 403, 429
DELETE /api/api-keys/{key_id} member+ - Odwoluje klucz API. 204, 401, 403, 404, 429

Schemas

Schematy publikacji

Zrodlem kontraktu jest app.schemas.publication.PublicationCreate. Opcje wspolne przekazuj przez common_content.platform_options, a opcje pojedynczego targetu przez overrides[account_id].platform_options.

PublicationCreate
{
  "publication_type": "post|story|reel|film",
  "targets": ["<account_id>"],
  "common_content": {
    "caption": "Hello world",
    "hashtags": ["smm", "automation"],
    "media_asset_ids": ["<media_asset_id>"],
    "title": "optional (required for film)",
    "description": "optional",
    "cover": { "kind": "frame_at_seconds", "t": 1.5 },
    "platform_options": {
      "youtube": { "privacy": "unlisted", "tags": ["api"] },
      "tiktok": { "privacy_level": "SELF_ONLY", "disable_comment": true },
      "x": { "content_type": "thread", "thread": ["tweet 1", "tweet 2"] },
      "linkedin": { "content_type": "article", "url": "https://example.com" }
    }
  },
  "overrides": {
    "<account_id>": {
      "caption_override": "...",
      "hashtags_override": ["..."],
      "media_override": ["<media_asset_id>"],
      "story_options": { "link_sticker_url": "https://example.com" },
      "cover_override": { "kind": "asset", "asset_id": 123 },
      "title_override": "...",
      "description_override": "...",
      "tags_override": ["yt", "tags"],
      "share_to_feed": true,
      "platform_options": { "privacy": "private" },
      "scheduled_at": "2026-07-01T15:00:00+02:00",
      "target_ig": true,
      "target_fb": false
    }
  },
  "schedule_mode": "now|next_slot|custom",
  "scheduled_at": "2026-07-01T15:00:00+02:00",
  "scheduled_at_per_target": {
    "<account_id>": "2026-07-01T15:00:00+02:00"
  }
}

Adapter-facing keys

Platform key Common fields
youtubetitle, description, tags, category / category_id, privacy / privacy_status
tiktokcontent_type, privacy_level, disable_duet, disable_comment, disable_stitch, cover_ms, cover_index
x / twittercontent_type, thread
linkedincontent_type, url, title, visibility
instagram_facebookfb_title, fb_comment_text, is_trial, share_to_feed; uzyj target_ig / target_fb dla fan-out IG vs FB.
PublicationCreateResponse
{
  "publication_id": "<uuid5 of (organization, idempotency_key)>",
  "request_id": "<X-Request-Id>",
  "jobs": [
    {
      "target_id": "<account_id>",
      "job_id": "<publish_job_id | null>",
      "status": "scheduled|queued|succeeded|failed",
      "scheduled_at": "ISO-8601 | null",
      "error_code": "string | null",
      "error_message": "string | null"
    }
  ],
  "summary": { "error_code": "all_targets_failed" }
}

Live reference

Capabilities

Aktualna macierz platforma x typ publikacji i limity mediow sa wystawione przez GET /api/publications/capabilities. Poniewaz macierz zmienia sie wraz z platformami i limitami, kanoniczna odpowiedz powinna byc pobierana runtime.

Token sluzy tylko do tego requestu w przegladarce i nie jest zapisywany.

Response shape lub wynik live
{
  "platforms": {
    "instagram_facebook": {
      "supported_types": ["post", "reel", "story"],
      "caption_limits": { "post": 2200, "reel": 2200, "story": 0 },
      "platform_options": {
        "content_type": { "type": "enum", "values": ["auto", "image", "video"] },
        "fb_title": { "type": "string", "max_length": 255 }
      }
    }
  },
  "media_limits": {
    "post": { "types": ["image/jpeg", "image/png", "image/webp"], "max_mb": 10, "count_max": 10 },
    "reel": { "types": ["video/mp4", "video/quicktime"], "max_mb": 500, "count_max": 1 }
  },
  "story_options": {
    "instagram_facebook": {
      "link_sticker_url": { "type": "url", "max_length": 2000 }
    }
  }
}

Examples

Przyklady integracji

Zastap smm_REPLACE_ME realnym kluczem API. Requesty kieruj do https://app.mumro.io.

Utworz post
cURL
curl -X POST https://app.mumro.io/api/publications \
  -H "Authorization: Bearer smm_REPLACE_ME" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: f47ac10b-58cc-4372-a567-0e02b2c3d479" \
  -d '{
    "publication_type": "post",
    "targets": [42, 43],
    "common_content": {
      "caption": "Hello from the API",
      "hashtags": ["smm", "automation"],
      "media_asset_ids": [1234]
    },
    "schedule_mode": "now"
  }'
Python requests
import os, uuid, requests

resp = requests.post(
    "https://app.mumro.io/api/publications",
    headers={
        "Authorization": f"Bearer {os.environ['SMM_API_KEY']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "publication_type": "post",
        "targets": [42, 43],
        "common_content": {
            "caption": "Hello from Python",
            "hashtags": ["smm", "automation"],
            "media_asset_ids": [1234],
        },
        "schedule_mode": "now",
    },
    timeout=30,
)
resp.raise_for_status()
print(resp.json())
Node.js fetch
const idempotencyKey = crypto.randomUUID();
const resp = await fetch("https://app.mumro.io/api/publications", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SMM_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": idempotencyKey,
  },
  body: JSON.stringify({
    publication_type: "post",
    targets: [42, 43],
    common_content: {
      caption: "Hello from Node",
      hashtags: ["smm", "automation"],
      media_asset_ids: [1234],
    },
    schedule_mode: "now",
  }),
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
console.log(await resp.json());
Zaplanuj reel na nastepny wolny slot
cURL
curl -X POST https://app.mumro.io/api/publications \
  -H "Authorization: Bearer smm_REPLACE_ME" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: f47ac10b-58cc-4372-a567-0e02b2c3d479" \
  -d '{
    "publication_type": "reel",
    "targets": [55],
    "common_content": {
      "caption": "Reel for next slot",
      "media_asset_ids": [9001]
    },
    "schedule_mode": "next_slot"
  }'
Jeden request, wiele platform i natywne opcje
cURL
curl -X POST https://app.mumro.io/api/publications \
  -H "Authorization: Bearer smm_REPLACE_ME" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "publication_type": "post",
    "targets": [42, 77, 88],
    "common_content": {
      "caption": "Launch update",
      "media_asset_ids": [1234, 1235],
      "platform_options": {
        "x": {
          "content_type": "thread",
          "thread": ["Launch update", "More details in the link"]
        },
        "linkedin": {
          "content_type": "article",
          "url": "https://example.com/launch",
          "title": "Launch update"
        },
        "tiktok": {
          "content_type": "carousel",
          "privacy_level": "SELF_ONLY",
          "disable_comment": false
        }
      }
    },
    "overrides": {
      "77": {
        "caption_override": "LinkedIn-specific copy",
        "platform_options": { "visibility": "PUBLIC" }
      }
    },
    "schedule_mode": "now"
  }'
YouTube film z metadanymi
cURL
curl -X POST https://app.mumro.io/api/publications \
  -H "Authorization: Bearer smm_REPLACE_ME" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "publication_type": "film",
    "targets": [201],
    "common_content": {
      "title": "API release walkthrough",
      "description": "Long-form release video",
      "media_asset_ids": [555],
      "platform_options": {
        "youtube": {
          "privacy": "unlisted",
          "category": "28",
          "tags": ["mumro", "api", "release"]
        }
      }
    },
    "schedule_mode": "custom",
    "scheduled_at": "2026-07-15T18:00:00+02:00"
  }'
Story z link stickerem
cURL
curl -X POST https://app.mumro.io/api/publications \
  -H "Authorization: Bearer smm_REPLACE_ME" \
  -H "Content-Type: application/json" \
  -d '{
    "publication_type": "story",
    "targets": [101],
    "common_content": { "media_asset_ids": [7777] },
    "overrides": {
      "101": {
        "story_options": { "link_sticker_url": "https://example.com/landing" }
      }
    },
    "schedule_mode": "now"
  }'
Wylistuj kolejke z filtrami
cURL
curl -G https://app.mumro.io/api/publish-jobs \
  -H "Authorization: Bearer smm_REPLACE_ME" \
  --data-urlencode "status=queued" \
  --data-urlencode "status=scheduled" \
  --data-urlencode "platform=instagram_facebook" \
  --data-urlencode "limit=20" \
  --data-urlencode "offset=0"
Anuluj albo ponow job
Cancel
curl -X POST https://app.mumro.io/api/publish-jobs/123/cancel \
  -H "Authorization: Bearer smm_REPLACE_ME"
Retry
curl -X POST https://app.mumro.io/api/publish-jobs/123/retry \
  -H "Authorization: Bearer smm_REPLACE_ME"