Tools Reference
Confirmed actions
Section titled “Confirmed actions”Every tool that acts on the outside world on your behalf works in two calls:
inboxSendTool, commentActTool (reply, delete), automationManageTool
(pose, updatePose, removePose, replay, delete), webhookManageTool
(create, delete), postDeleteTool, contentDeleteTool and
postUpdateTool (status: schedule).
- The first call (without
confirmationToken) does nothing. It returnsneedsConfirmation: true, apreviewof exactly what would happen, and aconfirmationToken. - Show the user the preview and get an explicit yes.
- The second call, with the same parameters and the token, acts.
The token is bound to your workspace, the action and its exact parameters (another text, URL or comment is refused), it works once, and it expires after 15 minutes. Anything else needs a new first call.
integrationList
Section titled “integrationList”List all connected social media accounts (channels) for your organization.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
group |
string | No | Group (customer) ID from groupList. When provided, only channels belonging to that group are returned |
Returns:
| Field | Type | Description |
|---|---|---|
id |
string | Integration ID (use this when scheduling posts) |
name |
string | Display name of the account |
picture |
string | Profile picture URL |
platform |
string | Platform identifier: instagram-standalone, tiktok or youtube |
customer |
object | The group (customer) this channel belongs to, as { id, name }, omitted if the channel is not assigned to a group |
groupList
Section titled “groupList”List all groups (customers) for your organization. Use a group’s id with integrationList to filter channels down to a single group.
Parameters: None
Returns:
| Field | Type | Description |
|---|---|---|
id |
string | Group (customer) ID (pass to integrationList as group) |
name |
string | Group (customer) display name |
integrationSchema
Section titled “integrationSchema”Get the posting rules, character limits, required settings, and available helper tools for a specific platform. Call this before scheduling a post to understand what the platform expects.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
isPremium |
boolean | Yes | Whether the user has a premium subscription |
platform |
string | Yes | Platform identifier: instagram-standalone, tiktok or youtube (the tool accepts every identifier of the original project, but these are the channels UseNadia connects) |
Returns:
| Field | Type | Description |
|---|---|---|
rules |
string | Platform-specific posting rules and best practices |
maxLength |
number | Maximum character length for posts |
settings |
object | JSON schema of required settings for this platform |
tools |
array | Platform-specific helper tools (see triggerTool) |
The tools array contains helper functions specific to the platform, called through triggerTool.
Each tool in the array has:
| Field | Type | Description |
|---|---|---|
methodName |
string | Function name to pass to triggerTool |
description |
string | What the tool does |
dataSchema |
array | Parameters the tool accepts |
triggerTool
Section titled “triggerTool”Execute a platform-specific helper function. These are discovered through integrationSchema (its tools array) and fetch dynamic data a setting needs before a post can be scheduled.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
integrationId |
string | Yes | The integration ID from integrationList |
methodName |
string | Yes | The function name from integrationSchema tools |
dataSchema |
array | Yes | Key-value pairs of parameters for the function |
Each item in dataSchema:
| Field | Type | Description |
|---|---|---|
key |
string | Parameter name |
value |
string | Parameter value |
Returns: whatever the helper returns, usually a list of { label, value } options to put in a setting.
integrationSchedulePostTool
Section titled “integrationSchedulePostTool”Schedule, draft, or immediately publish posts to social media platforms. This is the main tool for creating content.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
socialPost |
array | Yes | Array of posts to create |
Each item in socialPost:
| Field | Type | Required | Description |
|---|---|---|---|
integrationId |
string | Yes | Integration ID from integrationList |
contentId |
string | No | The board card the post is made from, from contentListTool. Links the post to the card: the card moves to PUBLISHED by itself when the post goes live |
isPremium |
boolean | Yes | Whether the user has premium |
date |
string | Yes | UTC datetime (e.g., 2025-01-15T10:00:00.000Z) |
shortLink |
boolean | Yes | Whether to shorten links in the post |
type |
string | Yes | draft, schedule, or now |
postsAndComments |
array | Yes | The post content and optional comments |
settings |
array | Yes | Platform-specific settings from integrationSchema |
Each item in postsAndComments:
| Field | Type | Description |
|---|---|---|
content |
string | The text of the post, plain (see formatting below) |
attachments |
array | Array of image/media URLs |
Each item in settings:
| Field | Type | Description |
|---|---|---|
key |
string | Setting name |
value |
any | Setting value (prefer IDs over labels when available) |
Content Formatting
Section titled “Content Formatting”Send the text as a person would type it: plain text, one line per sentence,
an empty line between paragraphs. No HTML, no <p>, no <br>. Nadia formats it
for each platform and keeps your line breaks: an empty line in your text is an
empty line in the published caption, on Instagram as on TikTok.
Freelance en mission longue = CDI déguisé ? Non, et voilà pourquoi.
On me le dit souvent : « t'es pas un vrai entrepreneur ».
Abonne-toi à @vincentleserpent pour apprendre à coder.#freelance #developpeur #cdiHTML is still accepted, for clients that already send it (the dashboard, the public API): a content that carries a tag is stored as it is, with these tags only.
| Tag | Usage |
|---|---|
<p> |
One line; an empty <p></p> is an empty line |
<h1>, <h2>, <h3> |
Headings |
<strong> |
Bold text |
<u> |
Underlined text |
<ul>, <li> |
Lists |
Posts vs Comments
Section titled “Posts vs Comments”The first item of postsAndComments is the post. On the channels UseNadia connects:
instagram-standalone: the following items are posted as comments under the publication, text only — an Instagram comment cannot carry media;tiktok: extra items are ignored, only the first one is published;youtube: extra items are ignored too.
Multiple Posts
Section titled “Multiple Posts”To schedule multiple posts (e.g., 20 posts across different days), add multiple items to the socialPost array, each with its own date and integrationId.
Returns:
| Field | Type | Description |
|---|---|---|
postId |
string | The created post ID |
integration |
string | The integration it was posted to |
If validation fails, returns { errors: string } with details about what went wrong (e.g., content exceeds character limit).
postsListTool
Section titled “postsListTool”Read the calendar: the organization’s posts (drafts, scheduled and already published) between two dates, or a single post in full. Read-only.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
postId |
string | No | Only this post, in full, with its thread. The dates are then ignored |
startDate |
string | Without postId |
UTC date for the start of the range (e.g. 2026-07-20T00:00:00Z) |
endDate |
string | Without postId |
UTC date for the end of the range (e.g. 2026-07-27T00:00:00Z) |
integrationId |
string | No | Only the posts of this channel, from integrationList |
Returns:
An object with a posts array. Each item has:
| Field | Type | Description |
|---|---|---|
id |
string | Post ID |
group |
string | The post group ID (shared across a post and its per-channel variations) |
state |
string | Post state (QUEUE, DRAFT, PUBLISHED, or ERROR) |
publishDate |
string | UTC datetime of the post (YYYY-MM-DD HH:mm) |
content |
string | The post text, without HTML. Over a date range, an excerpt of 280 characters; with postId, in full |
integrationId |
string | ID of the channel |
channel |
string | Display name of the channel |
platform |
string | Platform identifier (e.g. instagram-standalone, tiktok) |
thread |
string[] | With postId only: the comments posted under it, in order |
releaseId |
string | With postId only: the platform’s ID of the publication once published (the media ID of the Instagram tools), or missing when its media never reached the platform |
releaseURL |
string | With postId only: the publication’s URL |
missingMedia |
array | With postId only, when releaseId is missing: the files that never reached the platform (id, url) |
postUpdateTool
Section titled “postUpdateTool”Change what a post is without rewriting it: take a scheduled post out of the queue, put a draft back in it, or record the platform’s own ID of a post published outside UseNadia.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
postId |
string | Yes | The post, from postsListTool |
status |
string | No | draft (out of the queue) or schedule (back in the queue at its date) |
releaseId |
string | No | The platform’s ID of the publication (Instagram media ID, TikTok video ID…), so analytics, comments and automations find it |
confirmationToken |
string | status: schedule, second call |
The token of the first call, once the user said yes |
Putting a draft back in the queue (status: schedule) makes it go out at its date, so it is a confirmed action. Taking a post out of the queue and recording a releaseId are not.
Returns: { updated, needsConfirmation?, preview?, confirmationToken?, errors? }.
postDeleteTool
Section titled “postDeleteTool”Delete a post and everything scheduled with it (its per-channel variations and its thread). A scheduled post is cancelled; a published one is removed from UseNadia, not from the platform. Irreversible, so it is a confirmed action: the preview names the post’s date, channel and first words.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
postId |
string | One of the two | The post, from postsListTool |
group |
string | One of the two | The group of the post, from postsListTool |
confirmationToken |
string | Second call | The token of the first call, once the user said yes |
Returns: { deleted, group, needsConfirmation?, preview?, confirmationToken?, errors? }.
findSlotTool
Section titled “findSlotTool”The next free slot in a channel’s posting schedule (the time slots set on the channel, skipping those already taken). Read-only.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
integrationId |
string | No | The channel. Without it, the next free slot across the workspace |
Returns: { date } — UTC, ISO 8601, ready for integrationSchedulePostTool.
automationList
Section titled “automationList”List the comment → DM automations of the workspace (Instagram): the ids the Instagram settings of a post ask for, and what already runs on a publication. Read-only.
A post names its automation by an id in its Instagram settings: automationFlowId (a template drawn in the app), with automationKeywords and automationVariables. Words or values sent without the id are refused at scheduling, because nothing would run.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
flowId |
string | No | Only this template, with every publication it is posed on |
integrationId |
string | No | An Instagram channel, from integrationList. Also returns what runs on its publications |
postId |
string | No | With integrationId: only what runs on this publication. A post ID from postsListTool, or an Instagram media ID |
Returns:
| Field | Type | Description |
|---|---|---|
templates |
array | The automations drawn in the app: id (pass as automationFlowId), name, status (only ACTIVE ones run), ready, issues, trigger (post_comment or story_reply), keywordsRequired, variables (the names to fill in automationVariables), poses |
poses |
array | With integrationId: the templates running on the channel’s publications; with flowId alone: every publication that template is posed on. Each with id (the poseId of automationManageTool and automationRunsTool), flowId, flowName, postId, mediaId, keywords, variables, active, runs |
To put on a new post the automation of an earlier one, read that post’s pose with integrationId and postId, then schedule with the same automationFlowId and automationKeywords and only the values of automationVariables that change.
automationManageTool
Section titled “automationManageTool”Everything the public API does to a template or a pose, short of drawing the tree of steps (that is the editor in the app). One action per call.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
action |
string | Yes | pose, updatePose, removePose, poll, replay, activate, pause, delete, rename, createFromPreset |
flowId |
string | Per action | The template (pose, activate, pause, delete, rename) |
poseId |
string | Per action | The pose, from automationList (updatePose, removePose, poll, replay) |
integrationId |
string | pose |
The Instagram channel (also rename, createFromPreset) |
mediaId |
string | pose |
The publication already live, from commentsListTool |
kind |
string | No | post or story (default: the template’s trigger) |
keywords |
string[] | pose, updatePose |
The trigger words of this publication |
variables |
object | pose, updatePose |
One value per variable of the template |
active |
boolean | updatePose |
false pauses the pose |
commentIds |
string[] | replay |
1 to 50 comment IDs, from commentsListTool |
resend |
boolean | No | replay: restart a comment that already has a run |
name |
string | No | rename, createFromPreset |
preset |
string | createFromPreset |
lead_magnet, newsletter, link, qualify or blank |
language |
string | No | createFromPreset: fr (default) or en |
confirmationToken |
string | Second call | pose, updatePose, removePose, replay, delete: the token of the first call |
Returns: { ok, errors? } plus pose, template, poll (checked, started) or replay (one outcome per comment) depending on the action. A pose posed on a post you schedule does not go through here: pass automationFlowId, automationKeywords and automationVariables in the post’s Instagram settings.
pose, updatePose, removePose, replay and delete are confirmed actions: they make the account write to people, or cannot be undone. poll, activate, pause, rename and createFromPreset act at once. activate is refused while the template has errors.
automationRunsTool
Section titled “automationRunsTool”The people who went through a pose: one row per person, newest first. Read-only.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
poseId |
string | Yes | The pose, from automationList |
status |
string | No | PENDING, WAITING, DELAYED, DONE, HANDED_OFF, FAILED or SKIPPED |
Returns: { runs, total }. Each run: id, username, keyword, text (the comment or message that started it), status, email, tags, isFollower, error (Meta’s message on a failure), startedAt, completedAt.
commentsListTool
Section titled “commentsListTool”The Instagram side of a channel, live from Meta: recent publications, the stories live right now, or the comments under one publication. Read-only.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
integrationId |
string | Yes | The Instagram channel |
kind |
string | Yes | publications, stories or comments |
mediaId |
string | comments |
The publication: an Instagram media ID, or the releaseId of a post read with postsListTool |
Returns: publications (id — the media ID a pose watches —, type, caption, permalink, publishedAt, comments, likes) or comments (id, from as @username, text, at, replyTo, hidden), oldest first.
commentActTool
Section titled “commentActTool”Reply publicly to a comment, hide or unhide it, or delete it on Instagram.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
integrationId |
string | Yes | The Instagram channel |
commentId |
string | Yes | The comment, from commentsListTool |
action |
string | Yes | reply, hide, unhide or delete |
message |
string | reply |
The public answer, 2,200 characters max |
confirmationToken |
string | reply, delete |
The token of the first call, once the user said yes |
A reply is seen by everyone at once and a deletion is for good, so both are confirmed actions: the token is bound to this exact comment and text, single use, 15 minutes. Hiding is reversible and needs no confirmation.
Returns: { done, needsConfirmation?, preview?, confirmationToken?, errors? }.
inboxListTool
Section titled “inboxListTool”The direct message inbox (DMs) of an Instagram channel: the conversations, most recent first, each with the person, the date, its status in the inbox (NEEDS_REPLY = a human is waiting for an answer, AUTOMATED = an automation handled it) and its last message. Read-only.
Attachments are flattened: a card sent by an automation (ManyChat, UseNadia’s own flows) comes back as its text with its buttons and their links, a shared Reel or post as its permalink.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
integrationId |
string | Yes | The Instagram channel, from integrationList |
username |
string | No | Only the conversation with this person (@username or their ID). The search reads the 200 most recent conversations |
limit |
integer | No | How many conversations, 1 to 100. 25 by default |
Returns:
| Field | Type | Description |
|---|---|---|
channel |
string | Display name of the channel |
conversations |
array | Each with id (pass to inboxReadTool), with (@username of the person), recipientId, participants, updatedAt, status, replyWindowOpen (whether Instagram still accepts an answer: only within 24 h of the person’s last message) and lastMessage |
errors |
string | Why nothing came back (a channel that is not in the workspace, Meta’s error) |
Each message, here and in inboxReadTool: at, from (@username of the sender), text, attachments (kind — template, reel, share, story_reply, story_mention, image, video… — with text, url, storyId, buttons) and unsupported when the API does not show the message type (a voice note, a GIF).
Example: who is waiting for an answer.
{ "integrationId": "abc123", "limit": 50 }inboxReadTool
Section titled “inboxReadTool”One direct message conversation in full: its last 50 messages, oldest first. Get the conversation ID with inboxListTool first. Read-only.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
integrationId |
string | Yes | The Instagram channel |
conversationId |
string | Yes | The conversation, from inboxListTool |
Returns: channel and conversation: id, with, recipientId, updatedAt, replyWindowOpen, replyWindowClosesAt (when Instagram stops accepting an answer, 24 h after the person’s last message; after that only they can reopen the conversation by writing again) and messages, in the shape described under inboxListTool. Or errors.
Example:
{ "integrationId": "abc123", "conversationId": "t_1234567890" }inboxSendTool
Section titled “inboxSendTool”Send a direct message (DM) on an Instagram channel, in an existing conversation: Instagram only lets an account write to someone who wrote first.
A DM is seen by the person immediately and cannot be taken back, so it is a confirmed action. The first call sends nothing and returns a preview and a confirmationToken, signed over the exact channel, recipient and text, single use, valid 15 minutes. Once the user has said yes to that exact recipient and text, the second call with the same parameters and the token sends it. Changing the text needs a new first call.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
integrationId |
string | Yes | The Instagram channel |
conversationId |
string | One of the two | The conversation, from inboxListTool |
recipient |
string | One of the two | @username or ID of the person, when no conversationId |
text |
string | Yes | The message, plain text, 1 to 1,000 characters |
linkUrl |
string | No | A link, sent as a button under the text |
linkButtonText |
string | No | Label of that button, 20 characters max |
confirmationToken |
string | Second call | The token of the preview, only once the user said yes to that exact message |
Returns: { sent, needsConfirmation?, preview?, confirmationToken?, messageId?, errors? }. The preview holds channel, integrationId, conversationId, to, recipientId, text, linkUrl, linkButtonText and replyWindowClosesAt.
When the 24 h window is known to be closed, the first call already refuses with errors and nothing is proposed: only the person can reopen the conversation. Do not retry.
Example: first call, then the same with the token.
{ "integrationId": "abc123", "recipient": "@someone", "text": "Here is the guide", "linkUrl": "https://example.com/guide", "linkButtonText": "The guide" }inboxPollTool
Section titled “inboxPollTool”Read the inbox of an Instagram channel now, for what Meta’s webhook missed: new direct messages and story replies are picked up, and the automations waiting on them move on. Nothing re-reads the inbox on a schedule, so this is the catch-up. Nothing is sent.
Parameters: integrationId (string, required).
Returns: { polled, channel, conversations, handled, errors? }: conversations read from Meta, handled the inbound messages that reached a waiting automation.
generateImageTool
Section titled “generateImageTool”Generate an AI image to use as a post attachment.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
prompt |
string | Yes | Description of the image to generate |
Returns:
| Field | Type | Description |
|---|---|---|
id |
string | Media ID |
path |
string | URL of the generated image |
Use the returned path in the attachments array when scheduling a post.
generateVideoOptions
Section titled “generateVideoOptions”List all available video generation types and their required parameters.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
reasoning |
string | No | A short note on why the options are being listed. Ignored by the server, it only helps the agent’s trace |
Returns: { video }, an array of video generators, each with:
| Field | Type | Description |
|---|---|---|
type |
string | Video type identifier (e.g., image-text-slides, veo3) |
output |
string | Supported orientations: vertical|horizontal |
tools |
array | Helper functions (call with videoFunctionTool) |
customParams |
object | JSON schema of required parameters |
Available Video Types
Section titled “Available Video Types”| Type | Description | Requirements |
|---|---|---|
| Image Text Slides | Slide-based videos with text-to-speech | prompt, voice (get voice ID via videoFunctionTool) |
| Veo3 | AI-generated video with audio | prompt, optional images (max 3) |
videoFunctionTool
Section titled “videoFunctionTool”Execute helper functions for video generators. Use this to fetch required data before generating a video (e.g., listing available voices).
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
identifier |
string | Yes | Video type identifier from generateVideoOptions |
functionName |
string | Yes | Function name from the video type’s tools array |
Example: Call with identifier: "image-text-slides" and functionName: "loadVoices" to get available ElevenLabs voice IDs.
generateVideoTool
Section titled “generateVideoTool”Generate a video to use as a post attachment.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
identifier |
string | Yes | Video type from generateVideoOptions |
output |
string | Yes | vertical or horizontal |
customParams |
array | Yes | Key-value pairs of parameters for the video type |
Each item in customParams:
| Field | Type | Description |
|---|---|---|
key |
string | Parameter name |
value |
any | Parameter value |
Returns:
| Field | Type | Description |
|---|---|---|
url |
string | URL of the generated video |
Use the returned url in the attachments array when scheduling a post.
postMoveTool
Section titled “postMoveTool”Move an existing post to another date and time (UTC). Get the post ID from postsListTool first.
Moving is reversible — move it back — so unlike scheduling it does not go through a confirmation. A post that has already been published cannot be moved.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
postId |
string | Yes | The post to move |
date |
string | Yes | The new date, UTC (YYYY-MM-DD HH:mm:ss) |
Returns:
| Field | Type | Description |
|---|---|---|
moved |
boolean | Whether the post was moved |
postId |
string | The post ID |
date |
string | Its new date |
errors |
string | Why it was not moved |
videoPerformance
Section titled “videoPerformance”Read how the videos of a TikTok or Instagram channel perform, from the history UseNadia collects every 30 minutes. This tool has no equivalent in the original project, and it is the one that makes “how is my last video doing” answerable.
It is read-only and calls no provider: everything comes from the collected series. A channel with collection switched off answers collected: false with the reason.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
integrationId |
string | Yes | The channel whose videos to read |
videoId |
string | No | The provider video ID to focus on. Defaults to the newest |
limit |
number | No | How many of the newest videos to read, 1 to 30. Defaults to 10 |
Returns, per video: the current counters (views, reach, likes, comments, shares, saves, watch time), the engagement rates as a percentage of views, the current skip rate (skipRatePercent, Instagram Reels only, lower is better), the views reached at fixed ages (H+4, H+12, H+24, H+48, H+72, H+120, H+168) and the views gained over the last 24 and 48 hours.
The focus video is additionally compared with the others at its own age — rank and median — with a projection to H+120 from the channel’s own H+24 → H+120 multiplier, its current velocity, and the follower count with its 24 and 48 hour gain.
channelAnalyticsTool
Section titled “channelAnalyticsTool”The metrics of a channel over the last days, read live from the platform (cached one hour): followers, reach, impressions, views, likes… whatever the platform gives. With postId, the metrics of that one published post. Read-only.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
integrationId |
string | Yes | The channel |
days |
integer | No | The window in days (default 30) |
postId |
string | No | One published post of this channel instead of the account |
series |
boolean | No | true adds the day-by-day points of each metric |
Returns: { metrics }. Each metric: label, latest (the last value), total (the sum over the window), percentageChange against the previous window, and series when asked. With postId, missing: true says the post’s media never reached the platform.
analyticsHistoryTool
Section titled “analyticsHistoryTool”The raw history UseNadia collects every 30 minutes on a TikTok or Instagram channel, as time series: the account’s counters, or each video’s. For a ready-made reading, videoPerformance comes first; this one is for a chart, an export, or a question it does not answer. Read-only.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
integrationId |
string | Yes | The channel |
kind |
string | Yes | account or video |
videoId |
string | No | video: only this video (the platform’s ID) |
from, to |
string | No | ISO 8601. Default: the last 30 days |
limit, offset |
integer | No | Pagination |
Returns: { items, total, from, to }, plus videos (title, cover, duration of the IDs present) and hidden on the video series — the same shape as the history endpoints of the Public API.
contentListTool
Section titled “contentListTool”Read the content board: one card per video, in columns IDEA → SCRIPT → VALIDATED → TO_SHOOT → TO_EDIT → TO_SCHEDULE → PUBLISHED. Read-only. This tool has no equivalent in the original project.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
contentId |
string | No | Only this card |
stage |
string | No | Only the cards of this column |
withNumbers |
boolean | No | Add the numbers of the published cards (slower) |
Returns a contents array. Each card has id, externalId (your own ID when the card was created with one, otherwise null), title, stage, tags, hook, hookText (the text shown on screen over the first seconds), script and notes (in full), resourceStatus, resourceUrl, resourceBrief, integrationIds, plannedAt, hasVideo, scheduledPosts (channels of the post scheduled from it), publishedAt and, with withNumbers, numbers: views, engagementRate, followersGained, leads, emails.
contentCreateTool
Section titled “contentCreateTool”Add a card to the board. It lands at the bottom of its column. A card is visible and reversible on the board, so this tool writes directly, with no confirmation.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | Yes | Short title of the video |
stage |
string | No | The column to start in. Default IDEA |
hook |
string | No | The spoken hook, the first seconds |
hookText |
string | No | The text shown on screen over the first seconds |
script |
string | No | |
notes |
string | No | |
tags |
string[] | No | Free labels, e.g. a series |
resourceStatus |
string | No | The resource the video gives away: READY, or TO_CREATE (the card then stays out of TO_SCHEDULE and PUBLISHED) |
resourceUrl |
string | No | Where the resource lives, or will live |
resourceBrief |
string | No | What the resource holds, or the prompt to make it |
integrationIds |
string[] | No | Target channels, from integrationList |
plannedAt |
string | No | Planned publication date, UTC (YYYY-MM-DD HH:mm:ss) |
externalId |
string | No | Your own ID for the card (an import, a pipeline). With it the call is an upsert: the card holding this ID is updated with the fields sent instead of a second one being created |
Returns created (or updated on an upsert), the card’s id and stage, or errors.
contentUpdateTool
Section titled “contentUpdateTool”Edit a card and/or move it to another column, in one call. Only the fields given change. Get the card ID from contentListTool first.
Parameters: contentId (required), then any of stage (moves the card to the bottom of that column), title, hook, hookText, script, notes, tags (replaces the list), resourceStatus, resourceUrl, resourceBrief, integrationIds (replaces the list), plannedAt, postGroup (ties the card to a post already in the calendar: the group of that post, from postsListTool; each call adds one, and the card moves to PUBLISHED by itself when the first of its posts goes live).
Returns updated, the card’s id and stage, or errors when the card does not exist in the workspace, or when its resource is still TO_CREATE and the move goes to TO_SCHEDULE or PUBLISHED (the fields are written, the card stays where it is).
contentDeleteTool
Section titled “contentDeleteTool”Delete a card of the board. The posts scheduled from it stay in the calendar. It cannot be undone from the app, so it is a confirmed action: the preview names the card.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
contentId |
string | Yes | The card to delete |
confirmationToken |
string | Second call | The token of the first call, once the user said yes |
Returns { deleted, needsConfirmation?, preview?, confirmationToken?, errors? }; errors when the card does not exist in the workspace.
hooksFormsTool
Section titled “hooksFormsTool”The tool to call when someone wants hooks for a video. Returns a catalogue of hook forms (families) for the video being prepared, each with real hooks from the library — one on the subject itself when the library has one, the others drawn at random among the hooks of the family. Two calls never give the same examples. Read-only.
The forms are ordered by their measured effect on that nature of video, but the order is weak. Views never certify a hook: they follow the size of the subject, so normViews is information, not a ranking. The method: say what the video delivers and its subject, write one candidate per form by adapting the form of the example (never its subject), let the person pick. Never invent a hook from scratch, never copy one verbatim, never name a creator.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
nature |
string | No | What the video delivers: tuto_methode, ressource_outil, liste_conseils, explication_concept, actu, opinion, story_perso, story_tiers, demo_build, humour_sketch, vie_coulisses. Omitted: the whole corpus |
q |
string | No | Words of the subject (accents and case ignored) |
language |
string | No | fr (default), en or all |
forms |
integer | No | How many forms, 1 to 15. Default 7 |
examples |
integer | No | Real hooks per form, 1 to 5. Default 2 |
Returns: { forms }. Each form: family, label, measuredOn (the nature the numbers come from, all = the whole corpus), ratio (views vs the rest at equal baseline), bangers (share of videos at twice the baseline or more), accountsFor, accountsAgainst, verdict and examples — each with hook (the first sentence), opening (the first ~40 words, context only), normViews (5 = five times what that account usually does), year, nature, subject and onSubject (whether this hook is about the subject asked).
Example:
{ "nature": "liste_conseils", "q": "malte expatriation", "forms": 5 }hooksSearchTool
Section titled “hooksSearchTool”Search the library of real hooks that worked: about 26,000 first sentences of videos, each with its views at the baseline of its account. For “give me hooks for my video”, hooksFormsTool comes first; this one is the search by words, family or nature. Read-only. Experimental, like hooksFormsTool.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
q |
string | No | Words of the subject, any of them. Hooks matching the most words come first |
family |
string | No | Only this hook family (slug): promesse_directe, ciblage_si_tu, ressource_cadeau, interdiction_erreur, reset_fantasy, story_perso, story_tiers, verite_cachee, peur_menace, avant_apres, defi_demo, opinion_polemique, actu_nouveaute, comparatif_meilleur, obligation_urgence, chiffre_fait_choc, question… |
nature |
string | No | Only videos of this nature (same values as hooksFormsTool) |
language |
string | No | fr (default), en or all |
minNorm |
number | No | Floor on normalised views. Default 2 (at least twice the baseline of its account); lower it to 1.5 when the subject is rare |
limit |
integer | No | 1 to 100. Default 15 |
withFamilies |
boolean | No | Also return the measured effect of each hook family |
Returns: total (the size of the whole library) and hooks, each with year, language, hook, opening, views, normViews, family, nature, concrete, subject, lockinGestures and matchedWords. With withFamilies, families: family, n, accounts, ratio, bangers, accountsFor, accountsAgainst, verdict — only a family whose verdict is proven is a rule, the others are chosen for fit.
Example:
{ "q": "impôts malte", "minNorm": 1.5, "withFamilies": true }musicPickTool
Section titled “musicPickTool”Royalty-free music for a video: public domain and CC-BY tracks only, each file prepared (dynamics flattened, normalised to a known loudness) and downloadable at its url. Use it when a video needs a bed under the voice. Read-only. Experimental, like the hooks tools.
Pillars: piano (storytelling, educational — the default when in doubt), news (tension, reveal), synthwave (tech, tools, AI), epique (cinematic, big announcement). Give durationS so that tracks at least as long come first: a shorter track loops, with an audible seam on a crescendo. When creditLine is not null, it goes at the bottom of the description, before the hashtags — the licence depends on it.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
pillar |
string | No | piano, news, synthwave or epique. Omitted: the whole catalogue |
durationS |
number | No | Length of the video in seconds: tracks at least this long come first |
q |
string | No | Words of a title or an artist |
moreChoice |
boolean | No | Also return the limite tracks (a bit fast, or a punch early on: usable, less safe under a voice) |
limit |
integer | No | 1 to 100. Default 10 |
Returns: total (tracks in the whole catalogue), howTo (level, mix and credit: the same recipe for every track) and tracks, each with slug, title, artist, pillar, durationS, bpm, intro, verdict (ok or limite), verdictWhy, license, creditLine (paste as is, or null), url (the prepared mp3) and storedLufs (integrated loudness of the file).
Example:
{ "pillar": "piano", "durationS": 95 }uploadFromUrlTool
Section titled “uploadFromUrlTool”Import an image or video into the media library from a URL. The server downloads the file itself: the bytes never go through the MCP call or the assistant.
Use it before scheduling a post when the media lives somewhere else: an attachment that is not already hosted on UseNadia’s own domain fails upload-domain validation. The URL must be HTTPS and only has to be reachable from the server without a login — a public URL, or a signed URL that expires (R2, S3, a video another tool handed over with its download link). Private, loopback and link-local addresses are refused, after each redirect too.
The file goes through the same checks as a confirmed direct upload (real type sniffed from the bytes, ffprobe) and comes back with its numbers, so the caller can compare them with what the source said: a different size means the download is not the file that was meant.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
url |
string | Yes | The HTTPS URL of the image or video, reachable without a login |
Returns:
| Field | Type | Description |
|---|---|---|
id |
string | Media ID |
path |
string | Hosted URL — use this in the attachments array |
contentType |
string | video/mp4, image/jpeg, image/png, image/webp or image/gif — the type read from the bytes |
size |
number | Bytes stored |
duration |
number | null | Seconds for a video, null for an image |
width, height |
number | Displayed dimensions, rotation applied |
codec |
string | Picture codec (h264, hevc, png…) |
Accepted types and size limit are those of createUploadUrl (500 MB by default). Errors carry a code: URL_UNREACHABLE (not public, expired, or answered an error), FILE_TOO_LARGE, UNSUPPORTED_CONTENT_TYPE, FILE_CORRUPTED, DOWNLOAD_INTERRUPTED.
createUploadUrl
Section titled “createUploadUrl”Get a one-time URL to upload a file that sits on the client’s own disk — a video cut and compressed in an AI assistant’s code sandbox, for example — when there is no public URL to give uploadFromUrlTool. The bytes never go through the MCP call: the client PUTs them with curl, then calls confirmUpload.
The sequence: createUploadUrl → run the returned curl command → confirmUpload → integrationSchedulePostTool (or splitVideoIntoStories).
Every upload URL lives on the app’s own host (https://app.usenadia.com/api/uploads/…): it is the only host a sandbox’s network allow-list needs. It is valid 15 minutes, for one file, of the declared type and size, in the organization that asked for it, and gives access to nothing else.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
filename |
string | Yes | Name or path of the file on the client’s disk, reused in the curl command; its base name becomes the media name |
contentType |
string | Yes | video/mp4, image/jpeg, image/png, image/webp or image/gif |
size |
number | Yes | Exact size in bytes. 500 MB maximum by default (NADIA_DIRECT_UPLOAD_MAX_BYTES) |
Returns:
| Field | Type | Description |
|---|---|---|
uploadId |
string | To pass to confirmUpload |
uploadUrl |
string | Where to PUT the file |
method |
string | Always PUT |
headers |
object | The exact headers to send: Content-Type and Content-Length |
expiresAt |
string | ISO date, 15 minutes after the call |
maxSize |
number | The size limit of this server, in bytes |
curl |
string | The command to run as is, e.g. curl --fail-with-body -X PUT -T 'reel.mp4' -H 'Content-Type: video/mp4' 'https://app.usenadia.com/api/uploads/…' |
The PUT answers 201 with { uploadId, size }, or an error whose message starts with a code: UPLOAD_URL_EXPIRED (410), UPLOAD_URL_ALREADY_USED (409), CONTENT_TYPE_MISMATCH (415), SIZE_MISMATCH (400), FILE_TOO_LARGE / SIZE_EXCEEDED (413), UPLOAD_URL_INVALID (404). A transfer that fails halfway does not use the URL up: run the same command again.
confirmUpload
Section titled “confirmUpload”Finish an upload once its PUT answered 201. Checks the size, that the bytes are the declared type, and for a video that ffprobe can read it (codec, duration, dimensions as displayed — the rotation of a phone video is applied). Then adds the file to the media library. Calling it again returns the same media.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
uploadId |
string | Yes | From createUploadUrl |
Returns:
| Field | Type | Description |
|---|---|---|
id |
string | Media ID |
path |
string | Hosted URL — use it in attachments, or as videoUrl in splitVideoIntoStories |
contentType |
string | The declared type, checked |
size |
number | Bytes |
duration |
number | null | Seconds, null for an image |
width / height |
number | As displayed |
codec |
string | e.g. h264 |
Errors: FILE_NOT_UPLOADED, UPLOAD_IN_PROGRESS, UPLOAD_URL_EXPIRED, FILE_TYPE_MISMATCH, FILE_CORRUPTED, SIZE_MISMATCH, UPLOAD_NOT_FOUND. Uploads never confirmed are deleted after 24 hours.
mediaList
Section titled “mediaList”List the media library, newest first. Read only, limited to your workspace. This is how an agent finds the video you just uploaded from your phone without asking you for a link.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
type |
string | No | video or image; both when omitted |
search |
string | No | Part of the file name, case insensitive |
since |
string | No | Only media uploaded at or after this UTC date (ISO 8601) |
limit |
number | No | 20 by default, 100 at most |
cursor |
string | No | nextCursor of the previous call, for the next page |
Returns media and nextCursor (null on the last page). Each media:
| Field | Type | Description |
|---|---|---|
id |
string | Media ID |
path |
string | Hosted URL: an attachments entry of integrationSchedulePostTool, the input of transcribe, cutVideo and splitVideoIntoStories |
filename |
string | Original file name |
type |
string | video or image |
size |
number | Bytes, null if unknown |
createdAt |
string | Upload date, ISO 8601 UTC |
duration |
number | Videos only: seconds |
width, height |
number | Videos only: displayed size, rotation applied (height > width is vertical) |
Size, length and dimensions are read with ffprobe the first time a media is listed, then kept.
transcribe
Section titled “transcribe”Transcribe a video of the library with ElevenLabs Scribe (language detected automatically), with timestamps.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
path |
string | One of the two | Path from mediaList |
mediaId |
string | One of the two | Or the media ID |
includeWords |
boolean | No | Also return every word with its timestamps. false by default |
Returns:
| Field | Type | Description |
|---|---|---|
mediaId, path |
string | The video transcribed |
language |
string | Detected language (ISO 639) |
cached |
boolean | true when read back from an earlier transcription |
text |
string | The full transcript |
segments |
array | One { start, end, text } per sentence, in seconds |
words |
array | Only with includeWords: { start, end, text } per word |
cutVideo
Section titled “cutVideo”Cut a video of the library at exact timestamps. Each part is re-encoded
(H.264 + AAC, yuv420p, +faststart, rotation applied, original resolution)
and added to the library. Give exactly one of cuts or segments.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
path |
string | Yes | Path of the mp4 from mediaList |
cuts |
number[] | One of the two | Seconds where to slice: [24.45] gives two parts covering the whole video |
segments |
array | One of the two | Passages to keep, [{ start, end }]: each becomes a part, in the order given |
Every part lasts at least 0.5 s, 20 parts at most, and every timestamp falls inside the video.
Returns duration (of the original) and parts, in order, each with
id, path, start, end and duration.
splitVideoIntoStories
Section titled “splitVideoIntoStories”Cut one video of the media library into a series of stories of about the same length: 20 seconds unless another length is asked for, between 10 and 30. The cuts land in a silence of the video when there is one close enough, at even intervals otherwise, and the stories follow each other. The length is a target, not a slice: a few seconds left over are merged into the stories instead of making a tiny last one (2 min 02 at 20 s gives 6 stories of about 20 s; 30 s gives 2 of 15).
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
videoUrl |
string | Yes | URL of the mp4: a path from mediaList, uploadFromUrlTool or confirmUpload, or the URL of a video attached in the chat |
targetSeconds |
number | No | Target length of each story in seconds, 10 to 30. Default 20 |
removeSilences |
boolean | No | Take the long pauses out of the video before cutting it. Default false: an agent only turns it on when the user explicitly asks to remove the silences, never on its own and never as a suggestion |
Returns: duration (of the video cut), silencesRemoved (seconds of silence taken out, 0 when not asked) and stories, in order, each with id, path (the attachment of its own story post), start, end and endsOn: silence (the cut fell in a pause), even (no silence close enough, cut at an even interval) or end (last story).
Each story is then scheduled as its own post with integrationSchedulePostTool: one socialPost per story, in order, exactly one attachment, an empty content and the Instagram setting post_type = story, one minute apart so each is published before the next.
Example:
{ "videoUrl": "https://app.usenadia.com/uploads/2026/10/reel.mp4", "targetSeconds": 15 }notificationsListTool
Section titled “notificationsListTool”The workspace’s notifications, newest first (the bell of the app): a post published or failed, a channel to reconnect, an email captured by an automation… 100 per page. Read-only.
Parameters: page (integer, optional, 0 = the newest).
Returns: { notifications, total, page, hasMore }. Each notification: id, content (plain text), link, at.
webhooksListTool
Section titled “webhooksListTool”The outgoing webhooks of the workspace and the catalogue of events a webhook can subscribe to. Read-only.
Parameters: webhookId (string, optional, only this one).
Returns: { webhooks, events }. Each webhook: id, name, url, events (empty = every event), integrations (empty = every channel), hasSecret. The signing secret itself is shown once, by webhookManageTool on creation.
webhookManageTool
Section titled “webhookManageTool”Register, test or unregister an outgoing webhook.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
action |
string | Yes | create, test or delete |
webhookId |
string | test, delete |
The webhook |
event |
string | No | test: the event to simulate (default: the webhook’s first event, or post.published when it receives all) |
name |
string | create |
|
url |
string | create |
A public HTTPS URL |
events |
string[] | No | create: the events to receive (empty = all), names from webhooksListTool |
integrationIds |
string[] | No | create: the channels (empty = all) |
confirmationToken |
string | create, delete, second call |
The token of the first call, once the user said yes |
create and delete are confirmed actions: a webhook sends everything it subscribes to (DMs, captured emails…) to its URL for as long as it lives. test acts at once.
Returns: { ok, errors? } plus webhook on creation — with its secret, returned once: deliveries are signed with it (X-Nadia-Signature, HMAC SHA-256 of the body).
test sends one delivery to that webhook only, without any real event: the normal signed envelope with test: true and a sample data (fake test_… ids, nothing is published). It waits for the answer — one attempt, no retry, 10-second timeout — and returns { ok, delivered, status, event, deliveryId }: status is the HTTP status the URL answered, null when nothing answered. Same as POST /public/v1/webhooks/{id}/test.