Skip to content

API Overview

The Public API is everything the app does, over HTTP: the composer, the agent, the CLI, the n8n node and the MCP server all go through it. It is a fork of the original project’s API, with the same core routes and several resources the original project does not have.

UseNadia has its own Node.js SDK (@usenadia/node) and its own n8n node (n8n-nodes-nadia). Neither is published to npm yet. The original project’s packages are not a substitute: they talk to its API, not to this one.

There are two ways to authenticate:

Your API key lives in UseNadia under Settings → Developers, in the Access section, under API Key. Click the reveal icon to show it, then copy. You can rotate it from the same place, rotating immediately invalidates the old key, so update anything using it (scripts, n8n, MCP clients, the CLI) at the same time.

Include it in the Authorization header:

Terminal window
curl -H "Authorization: your-api-key" https://app.usenadia.com/api/public/v1/integrations

If you’re building an app for other UseNadia users, use OAuth2 Authentication to get tokens that act on behalf of users. OAuth tokens start with pos_ and are used the same way:

Terminal window
curl -H "Authorization: pos_your-oauth-token" https://app.usenadia.com/api/public/v1/integrations

The base URL is https://app.usenadia.com/api.

A successful GET, PUT or DELETE answers 200; a successful POST answers 201. Errors carry { "msg": "…" }, except a post the platform refuses, which carries { provider, name, error }.

Status Meaning
400 Bad Request The body or a parameter is malformed (wrong shape, unknown enum, missing required field), or the platform refused the post.
401 Unauthorized Authorization header is missing, or the API key is unrecognised.
403 Forbidden The key is valid but may not do this (the music write routes, for instance).
404 Not Found The route doesn’t exist, or the channel, post or record in the path is not in your organisation.
409 Conflict A content card with that externalId already exists, or a card cannot move there yet.
413 Payload Too Large Your body exceeded 50 MB on /posts, usually because images were base64-inlined instead of pre-uploaded. See Uploads troubleshooting.
429 Too Many Requests You exceeded the hourly limit on POST /posts.
5xx Server error, retry with exponential backoff.

For DELETE routes, 404 always means “already deleted” and is safe to ignore. Treat other 500 responses as real server errors: log them, retry with exponential backoff, and don’t silently suppress them.

Six of these resource groups do not exist in the original project: the content board, automations, inbox, the collected analytics, webhooks, and hooks and music. Per-platform settings for a post are on Instagram and TikTok.

Three channels can be connected here. Two of them publish:

Platform __type Key settings
Instagram instagram-standalone post_type, is_trial_reel, graduation_strategy, share_to_feed, collaborators, automationFlowId, automationKeywords, automationVariables
TikTok tiktok content_posting_method, privacy_level, duet, stitch, comment, autoAddMusic, brand_content_toggle, brand_organic_toggle, video_made_with_ai, title
YouTube youtube Connectable for content cards and statistics. Publishing waits on Google’s API audit

When creating posts, the settings object must include a __type field matching the channel’s platform.

The easiest way to generate your post payloads is the composer itself: this is the same wizard the app uses to schedule posts, except that it generates the JSON payload for your API requests instead of scheduling.

Make sure you are logged in to app.usenadia.com.

Open in full screen

Terminal window
# Step 1: upload
curl -X POST "https://app.usenadia.com/api/public/v1/upload" \
-H "Authorization: your-api-key" \
-F "file=@reel.mp4"
# 201 → { "id": "cm4med0001", "path": "https://media.usenadia.com/k3J9aQ2xZp.mp4", ... }
# Step 2: create the post with the uploaded media
curl -X POST "https://app.usenadia.com/api/public/v1/posts" \
-H "Authorization: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"type": "schedule",
"date": "2026-11-03T10:00:00.000Z",
"shortLink": false,
"tags": [],
"posts": [{
"integration": { "id": "your-instagram-id" },
"value": [{
"content": "Trois choses que personne ne dit sur Malte.",
"image": [{ "id": "cm4med0001", "path": "https://media.usenadia.com/k3J9aQ2xZp.mp4" }]
}],
"settings": { "__type": "instagram-standalone", "post_type": "post" }
}]
}'
# 201 → [ { "postId": "cm4post0001", "integration": "your-instagram-id" } ]
{
"type": "now",
"date": "2026-11-03T10:00:00.000Z",
"shortLink": false,
"tags": [],
"posts": [
{
"integration": { "id": "your-tiktok-id" },
"value": [
{
"content": "Trois choses que personne ne dit sur Malte.",
"image": [{ "id": "cm4med0001", "path": "https://media.usenadia.com/k3J9aQ2xZp.mp4" }]
}
],
"settings": {
"__type": "tiktok",
"content_posting_method": "DIRECT_POST",
"privacy_level": "PUBLIC_TO_EVERYONE",
"duet": true,
"stitch": true,
"comment": true,
"autoAddMusic": "no",
"brand_content_toggle": false,
"brand_organic_toggle": false
}
}
]
}
{
"settings": {
"__type": "instagram-standalone",
"post_type": "story",
"automationFlowId": "<template id>",
"automationKeywords": ["LIEN", "LEIN", "GUIDE"],
"automationVariables": { "ressource": "le guide Malte", "bouton": "Le guide", "lien": "https://…" }
}
}

The pose is created when the post publishes, not when it is scheduled: Instagram has no media ID before then. The words and values are checked at scheduling time. See Automations.

GET /notifications?page=0

The organisation’s notifications, newest first, 100 per page (page is 0-indexed): a post published, a post that failed, a channel to reconnect. Response 200:

{
"notifications": [
{ "id": "a1b2c3d4-…", "content": "Your post to Instagram was published successfully", "link": null, "createdAt": "2026-10-20T10:30:00.000Z" }
],
"total": 250,
"page": 0,
"limit": 100,
"hasMore": true
}

For a push rather than a poll, register a webhook.