Skip to Content
ConnectAPI & Webhooks

API & Webhooks

Publish to your channels from your own systems, and hear when a post goes out or a channel stops working.

API keys

API access comes with the Growth and Business plans. Make a key in Studio → Connections → API keys (the owner or an admin). Give it a name and choose what it may do:

  • Integrator API: the /public/v1 publishing API below.
  • Content pipeline (MCP): lets scheduled AI runs read and write a brand’s plan. Most integrations don’t need it.

The key is shown once: copy it then. A key can be revoked at any time, and can be given an expiry. Keep it on your server; never put it in a web page or an app.

Send it with every request, as either header:

X-Connect-Api-Key: dck_… Authorization: Bearer dck_…

A missing or wrong key gets 401. A key without the right permission gets 403, and so does every request from a workspace whose plan does not include the API (Starter or the free trial): “API access comes with Growth and Business…”. A key made on Growth stops working if the workspace moves to Starter, and works again once it is back on Growth or Business.

The publishing API

Base URL: https://studio-api.duabaconnect.com/public/v1. Every call acts on the workspace the key belongs to. Bodies are JSON (up to 8 MB), except uploads.

The request and response shapes match Postiz’s public API, so a tool or n8n flow built for Postiz works by changing its base URL and key.

Method and pathWhat it does
GET /integrationsYour channels: id, name, identifier (the platform), picture, disabled, profile, customer. Add ?group= to filter by client
GET /integrations/:id/targetsWhere inside a channel a post can go: Pinterest boards, subreddits, Discord and Slack channels
POST /uploadUpload a file (multipart field file, up to 200 MB). Returns the path to use in a post
POST /upload-from-urlUpload from a URL: { "url": "…" }
POST /postsCreate a post (below)
GET /postsYour posts and where each went; from, to, limit (up to 500)
DELETE /posts/:idDelete a post. A published post is also deleted on the platform where the platform allows it; otherwise the answer says it is still live
GET /find-slot/:idThe next free posting time for a channel, within 60 days
GET /groupsYour clients
POST /customersFind or create a client by name
GET /analytics/:integration?date=30A channel’s figures over the last N days
GET /analytics/post/:postIdOne post’s figures, channel by channel
GET /is-connectedChecks the key: { "connected": true }

A path that does not exist answers 404 with the list of routes. The API does not make images: make them in Studio’s Composer, or upload your own with POST /upload.

Creating a post

{ "type": "schedule", "date": "2026-11-03T09:00:00Z", "posts": [ { "integration": { "id": "<channel id>" }, "value": [ { "content": "Our new menu is out!", "image": [{ "path": "<path from /upload>" }] } ], "settings": {} } ] }
  • type is now, schedule or draft.
  • One entry in posts per channel; each can have its own words.
  • One value entry per post: threads are not supported, and neither is updating a post (delete it and create it again).
  • The same checks as Studio apply before anything is saved: caption length, each platform’s media rules, and Approval first on the channel.

Webhooks

Studio → Connections → Webhooks: we POST to your URL when something happens.

EventWhen
post.publishedA post went out
post.failedA post failed on a channel
channel.disabledA channel stopped working and needs reconnecting

Choose the events and the channels (none chosen means all). The body is:

{ "id": "<event id>", "event": "post.published", "createdAt": "…", "organizationId": "…", "data": { } }

Checking the signature

Each delivery carries X-Studio-Event, X-Studio-Event-Id and X-Studio-Signature: t=<unix time>,v1=<signature>. The signature is an HMAC-SHA256, with your webhook’s signing key (whsec_…, shown once when you create it), of the event id, the time and the raw body joined by dots. Check it before trusting the body, and reject old timestamps:

import crypto from 'node:crypto'; function verify(rawBody, headers, secret) { const id = headers['x-studio-event-id']; const parts = Object.fromEntries(headers['x-studio-signature'].split(',').map((p) => p.split('='))); const expected = crypto.createHmac('sha256', secret).update(`${id}.${parts.t}.${rawBody}`).digest('hex'); const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300; return fresh && expected.length === parts.v1?.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1)); }

A delivery that fails is tried up to five times. After 20 failures in a row the webhook is switched off, with the reason shown in Studio; turn it back on once your endpoint is fixed. Recent attempts shows each delivery and how it went.

Your own applications

Studio → Connections → Applications registers an OAuth application, so other people’s workspaces can grant it access with the usual sign-in and consent screen (authorization code with PKCE). Each workspace sees the applications it has approved there too, and can revoke them.

Last updated on