SotialMedia API
Schedule content across connected social accounts from your own systems. Every endpoint below is served under https://sotialmedia.com/api/v1 and described by the generated OpenAPI 3.1 document.
Authentication
Create an API key under your organization (API keys page). Keys look like sm_live_..., belong to one organization and can be limited to one workspace and to a set of scopes such as drafts.* or publications.*. Send the key on every request:
Authorization: Bearer sm_live_...Operations marked Console session are reserved for signed-in people because they must be attributed to a person: creating keys and approving publications.
Conventions
- JSON in and out, UTC timestamps in ISO 8601, ids are UUIDs ordered by creation time.
- Every response carries
X-Request-Id. Quote it when you contact support. - Lists accept
limit(1 to 200) andcursor; follownextCursoruntil it is null. - Each API key may make 600 requests per minute. Above that you get 429 with
Retry-AfterandX-RateLimit-Remaining. - Organization limits (connected accounts, scheduled publications, API requests) answer 402
quota_exceeded.
Errors
All errors share one shape. Codes: unauthorized, forbidden, not_found, conflict, validation_failed, idempotency_mismatch, invalid_state, quota_exceeded, rate_limited, mfa_required, upstream_error, internal.
{
"error": {
"code": "validation_failed",
"message": "Request validation failed",
"details": { "issues": [{ "path": "scheduledFor", "message": "Invalid input" }] }
},
"requestId": "4c1f0d2e-7a9b-4c3d-9e1f-2b3c4d5e6f70"
}Versioning
Versioning policy: the version is part of the path (/api/v1). Changes within v1 are additive only: new endpoints, new optional request fields, new response fields and new enum values may appear without notice, so clients must ignore unknown fields. Breaking changes ship as a new path version. Deprecated endpoints and versions are announced at least 90 days ahead and respond with a Sunset header (RFC 8594) carrying the retirement date, plus a Deprecation header, until they are removed.
Quickstart: draft, then schedule
1. Create a draft in a workspace. The draft is channel-agnostic; per-channel overrides are optional variants.
curl -X POST https://sotialmedia.com/api/v1/workspaces/$WORKSPACE_ID/drafts \
-H "Authorization: Bearer $SOTIALMEDIA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Launch day",
"body": "We are live. Read the announcement on our site."
}'2. Schedule it on a connected account. scheduledFor is either an instant with a zone offset or a wall clock time in timezone. Send an Idempotency-Key so a retried request never creates a second publication: the same key with the same body returns the original (with Idempotent-Replayed: true), the same key with a different body is rejected with 422 idempotency_mismatch.
curl -X POST https://sotialmedia.com/api/v1/workspaces/$WORKSPACE_ID/publications \
-H "Authorization: Bearer $SOTIALMEDIA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: launch-2026-11-02-x" \
-d '{
"draftId": "$DRAFT_ID",
"socialConnectionId": "$CONNECTION_ID",
"scheduledFor": "2026-11-02T09:00:00",
"timezone": "America/Denver"
}'3. Poll the publication or subscribe to webhooks. The detail response includes the status history and an attempt summary; platform level request details stay with operators.
curl https://sotialmedia.com/api/v1/workspaces/$WORKSPACE_ID/publications/$PUBLICATION_ID \
-H "Authorization: Bearer $SOTIALMEDIA_API_KEY"Webhooks
Register an HTTPS endpoint and choose event types (publication.scheduled, publication.published, publication.failed, publication.needs_attention, publication.cancelled, connection.connected, connection.revoked, connection.error). Each delivery is a POST with a JSON body { type, occurredAt, data } and the header:
X-SotialMedia-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">Verify with the signing secret shown once when the endpoint is created. After a rotation, deliveries carry two v1 values for 24 hours so you can switch secrets without downtime. Reject timestamps older than five minutes. Failed deliveries are retried with exponential backoff; the delivery log is available through the API and the console.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifySotialMediaSignature(rawBody, header, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
return header.split(",").some((p) => p.startsWith("v1=") && safeEqual(p.slice(3), expected));
}
function safeEqual(a, b) {
const x = Buffer.from(a, "hex"), y = Buffer.from(b, "hex");
return x.length === y.length && timingSafeEqual(x, y);
}Endpoints
System
Health and the contract itself. No authentication.
| Method | Path | Summary | Auth | Permission |
|---|---|---|---|---|
| GET | /api/v1/health | Health check Liveness with a database ping. Returns 503 when the database is unreachable. | Public | |
| GET | /api/v1/openapi.json | OpenAPI document This document. | Public | |
Me
The authenticated principal.
| Method | Path | Summary | Auth | Permission |
|---|---|---|---|---|
| GET | /api/v1/me | Current principal For an API key: its organization, workspace restriction and scopes. For a console session: the user and their organizations. | API key | |
Organizations
Organization details, workspaces and usage against entitlements.
| Method | Path | Summary | Auth | Permission |
|---|---|---|---|---|
| GET | /api/v1/organizations/{orgId} | Get organization | API key | org.read |
| GET | /api/v1/organizations/{orgId}/workspaces | List workspaces A workspace-scoped key only sees its own workspace. | API key | org.read |
| GET | /api/v1/organizations/{orgId}/usage | Usage and entitlements Current period counters per dimension with the soft and hard limits of the organization's plan. | API key | org.read |
Workspaces
A workspace holds one brand's connected accounts, media, drafts and publications.
| Method | Path | Summary | Auth | Permission |
|---|---|---|---|---|
| GET | /api/v1/workspaces/{workspaceId} | Get workspace | API key | workspace.read |
Connections
Connected social accounts. Credentials never leave the platform.
| Method | Path | Summary | Auth | Permission |
|---|---|---|---|---|
| GET | /api/v1/workspaces/{workspaceId}/connections | List connected accounts | API key | connections.read |
| GET | /api/v1/workspaces/{workspaceId}/connections/{connectionId} | Get connected account | API key | connections.read |
| DELETE | /api/v1/workspaces/{workspaceId}/connections/{connectionId} | Disconnect account Queues a disconnect job: the platform authorization is revoked where supported, credentials are discarded and pending publications on the account are cancelled. | API key | connections.manage |
Media
Upload flow: register, PUT to the signed URL, complete, poll until ready.
| Method | Path | Summary | Auth | Permission |
|---|---|---|---|---|
| GET | /api/v1/workspaces/{workspaceId}/media | List media assets | API key | media.read |
| POST | /api/v1/workspaces/{workspaceId}/media | Register a media asset Creates the asset in pending_upload and returns a signed URL valid for 10 minutes. PUT the file bytes there with the declared Content-Type, then call complete. | API key | media.write |
| GET | /api/v1/workspaces/{workspaceId}/media/{mediaId} | Get media asset Poll after completing an upload: status becomes ready or rejected (see rejectionReason). | API key | media.read |
| POST | /api/v1/workspaces/{workspaceId}/media/{mediaId}/complete | Complete an upload Marks the object uploaded and queues verification (type sniffing, size limits, checksum, malware scan when configured). | API key | media.write |
Drafts
Channel-agnostic content with optional per-channel variants.
| Method | Path | Summary | Auth | Permission |
|---|---|---|---|---|
| GET | /api/v1/workspaces/{workspaceId}/drafts | List drafts | API key | drafts.read |
| POST | /api/v1/workspaces/{workspaceId}/drafts | Create a draft | API key | drafts.write |
| GET | /api/v1/workspaces/{workspaceId}/drafts/{draftId} | Get a draft with variants | API key | drafts.read |
| PATCH | /api/v1/workspaces/{workspaceId}/drafts/{draftId} | Update a draft Bumps the version and clears stored validations; schedule again to re-validate. | API key | drafts.write |
| DELETE | /api/v1/workspaces/{workspaceId}/drafts/{draftId} | Archive a draft | API key | drafts.write |
| PUT | /api/v1/workspaces/{workspaceId}/drafts/{draftId}/variants/{channel} | Set channel overrides Replaces the overrides for one channel (text, media, channel settings). | API key | drafts.write |
| POST | /api/v1/workspaces/{workspaceId}/drafts/{draftId}/validate | Validate for a channel Runs the channel adapter's validation on the effective content and stores the result on the variant. | API key | drafts.write |
Publications
Scheduling a draft on a connected account, status history and controls.
| Method | Path | Summary | Auth | Permission |
|---|---|---|---|---|
| GET | /api/v1/workspaces/{workspaceId}/publications | List publications | API key | publications.read |
| POST | /api/v1/workspaces/{workspaceId}/publications | Schedule a publication Validates the draft for the account's channel, snapshots the content, records usage against the plan and queues delivery. Workspaces that require approval create the publication in needs_attention until approved. Supports Idempotency-Key | API key | publications.schedule |
| GET | /api/v1/workspaces/{workspaceId}/publications/{publicationId} | Get a publication | API key | publications.read |
| POST | /api/v1/workspaces/{workspaceId}/publications/{publicationId}/cancel | Cancel a publication Allowed until delivery starts. A publication already publishing answers 409. | API key | publications.cancel |
| POST | /api/v1/workspaces/{workspaceId}/publications/{publicationId}/retry | Retry a publication Re-queues a failed or needs_attention publication for immediate delivery. The account must be active. | API key | publications.retry |
| POST | /api/v1/workspaces/{workspaceId}/publications/{publicationId}/approve | Approve a publication Approvals are attributed to a person: console sessions only. The approver cannot be the person who scheduled it. | Console session | publications.approve |
API keys
Scoped keys for programmatic access. Managed from a console session.
| Method | Path | Summary | Auth | Permission |
|---|---|---|---|---|
| GET | /api/v1/organizations/{orgId}/api-keys | List API keys | Console session | org.api_keys.manage |
| POST | /api/v1/organizations/{orgId}/api-keys | Create an API key The plaintext key is returned once in this response. | Console session | org.api_keys.manage |
| DELETE | /api/v1/organizations/{orgId}/api-keys/{keyId} | Revoke an API key | Console session | org.api_keys.manage |
Webhooks
Signed event delivery to your HTTPS endpoints with secret rotation.
| Method | Path | Summary | Auth | Permission |
|---|---|---|---|---|
| GET | /api/v1/organizations/{orgId}/webhooks | List webhook endpoints | API key | webhooks.manage |
| POST | /api/v1/organizations/{orgId}/webhooks | Create a webhook endpoint The signing secret is returned once. Deliveries are POST requests with the WebhookEvent body and the X-SotialMedia-Signature header. | API key | webhooks.manage |
| GET | /api/v1/organizations/{orgId}/webhooks/{webhookId} | Get a webhook endpoint | API key | webhooks.manage |
| DELETE | /api/v1/organizations/{orgId}/webhooks/{webhookId} | Disable a webhook endpoint Stops deliveries; the delivery history stays readable. | API key | webhooks.manage |
| POST | /api/v1/organizations/{orgId}/webhooks/{webhookId}/rotate-secret | Rotate the signing secret Deliveries carry two v1 signatures for 24 hours so consumers can switch without downtime. | API key | webhooks.manage |
| GET | /api/v1/organizations/{orgId}/webhooks/{webhookId}/deliveries | List deliveries Delivery attempts for one endpoint, newest first, with the last response status and error. | API key | webhooks.manage |