Data model
Workspaces, channels, posts, and media — the mental model in five minutes.
Four resources cover almost everything the API does. Get these straight and every endpoint becomes predictable.
Workspace ─ the tenant your API key belongs to
├── Channels ─ connected social accounts (Instagram, LinkedIn, …)
├── Posts ─ content, each targeting exactly ONE channel
├── Media ─ imported images/videos, attachable to posts
└── Webhooks ─ endpoints that receive events about the aboveWorkspace — the boundary of everything
Every credential is bound to one workspace: an API key (nim_live_...) is created
inside a workspace, and an OAuth access token (nim_oat_...) is issued for the workspace
the user picked during consent. There is no cross-workspace access — a key can only see
the channels, posts, and media of its own workspace, and resources from other workspaces
return 404, not 403.
GET /v1/workspace tells you which workspace you're in and which scopes your credential
has (grantedScopes).
Channels — where posts go
A channel is one connected social account. The type field has 13 values across 9 platforms:
| Platform | Channel types |
|---|---|
FACEBOOK_PAGE, FACEBOOK_GROUP | |
INSTAGRAM_PROFESSIONAL, INSTAGRAM_PERSONAL | |
| Twitter/X | TWITTER |
LINKEDIN_PROFILE, LINKEDIN_PAGE | |
| TikTok | TIKTOK_PERSONAL, TIKTOK_BUSINESS |
| YouTube | YOUTUBE |
PINTEREST | |
| Google Business | GOOGLE_BUSINESS |
| Threads | THREADS |
Channels are connected in the Nimply app (the API can't connect them — that requires the
platform's own OAuth dance). Before publishing, check that isConnected is true and
tokenStatus is not NEEDS_RECONNECT. Each channel also has a timezone and a
posting schedule (GET /v1/channels/{id}/schedule) — the weekday time slots used by
"next_slot" scheduling.
Posts — one post, one channel
A post targets exactly one channel. When you call POST /v1/posts with several
channelIds, Nimply creates one post per channel and returns them all — each with its
own id, status, and lifecycle. Keep this in mind when you later schedule, poll, or
delete: you operate on individual per-channel posts.
Status lifecycle
request-approval
┌─────────────────────────────────────┐
│ ▼
DRAFT ◄───────── reject ─────── PENDING_APPROVAL
│ ▲ │
│ └── approve (no publish time) ──────┤
│ │ approve (has a
│ schedule / publish now / next_slot │ future publish time)
▼ │
SCHEDULED ◄──────────────────────────────┘
│
│ publish time reached
├──────────────► PUBLISHED (live on the platform)
└──────────────► FAILED (the platform rejected it)
ARCHIVED — archived in the Nimply app; find with ?status=ARCHIVED- DRAFT — created, nothing will happen until you act (
"schedule": "draft"is the default). - PENDING_APPROVAL — a draft sent for review via
POST /v1/posts/{id}/request-approval. Approving returns it toSCHEDULED(if it has a future publish time) orDRAFT; rejecting returns it toDRAFT. - SCHEDULED — queued for a publish time. Note that "publish now" is also asynchronous: the post briefly sits in
SCHEDULEDwhile the queue processes it. - PUBLISHED — successfully live. Published posts can't be edited or deleted via the API.
- FAILED — publishing was attempted and rejected by the platform.
- ARCHIVED — archived in the Nimply app; excluded from your normal workflow but still readable.
Content types
The contentType field selects the format on the platform:
| Value | Meaning |
|---|---|
POST | A standard feed post (default). Multiple media on a POST become a carousel — order follows mediaIds. |
REEL | A short-form vertical video (Reels-style surface). |
STORY | An ephemeral story. |
VIDEO | A regular video post (e.g. a YouTube upload — use title too). |
Platform-specific options
POST /v1/posts covers the fields every platform understands: content, title,
contentType, mediaIds, schedule. Four platforms support more than that, and each has
a dedicated create endpoint that accepts its full option set and validates
platform-required fields at request time:
| Platform | Endpoint | Notable options |
|---|---|---|
| YouTube | POST /v1/posts/youtube | title (required), videoType (video/short), category, visibility, license, notifySubscribers, allowEmbedding, madeForKids |
| TikTok | POST /v1/posts/tiktok | privacyLevel (required), allowComments, allowDuet, allowStitch, brandContent, brandOrganic |
POST /v1/posts/pinterest | boardId (required), link, altText | |
POST /v1/posts/linkedin | visibility (PUBLIC/CONNECTIONS) |
Two helper endpoints supply the values these options need:
GET /v1/channels/{id}/pinterest/boards
lists a channel's boards, and
GET /v1/channels/{id}/tiktok/creator-info
returns the privacy levels a TikTok account allows (plus Duet/Stitch availability and the
max video duration).
Rule of thumb: use the generic endpoint for simple cross-platform posting, and the platform endpoint whenever you need platform features. Facebook, Instagram, Twitter/X, and Threads have no extra options — the generic endpoint is all they need.
Media — upload once, attach anywhere
Media assets are imported by URL (POST /v1/media with a publicly reachable url) into
workspace storage, then referenced by id in a post's mediaIds array — in display order.
Assets are workspace-level, not post-level: one uploaded image can be attached to many
posts. Types are IMAGE, VIDEO, GIF, and DOCUMENT. The url returned on a media
asset is a signed delivery URL that expires after 24 hours — re-fetch the asset for a
fresh one, and don't store it.
Webhooks — events about posts and channels
Webhooks are registered per workspace and fire for post events
(post.created, post.updated, post.published, post.failed, post.deleted, and the
approval trio post.approval_requested / post.approved / post.rejected) and
channel events (channel.disconnected). Every delivery envelope carries the
workspaceId plus the affected postId/channelId, and is signed with your endpoint's
secret.