Most content APIs hand you a model and leave the hard parts — strategy, footage, captions, music licensing, scheduling — to you. This one hands you the finished artefact. Behind the endpoint is the same pipeline that runs the platform's own customers: a researcher that builds a content calendar from the brand's niche, a producer that renders 9:16 video, and a caption writer that emits a variant for all 11 platforms whether or not the brand has connected them.
The interface is deliberately small. Twenty tools, one webhook event, one frozen package schema. Nothing expensive happens before payment: creating a brand writes a row and stops, so an agent that provisions a hundred brands and abandons them costs a hundred rows.
By default the server is deliver-only: we produce and hand over, and your agent publishes wherever it likes. A brand's owner can also connect their own social accounts and ask us to post for them — call social_connect_link, give the URL to your user, and watch social_accounts_list. Publishing is theirs to grant and theirs to approve; your key never gains the power to post.
Quickstart
Every call below works against the live server. Replace the placeholders and run them in order; the whole funnel is ten calls.
-
1 Register
Unauthenticated. The key is returned in the response body and never sent by email — no email we send ever contains a credential.
curl -X POST https://api.myinfluenceai.com/mcp/register \ -H 'Content-Type: application/json' \ -d '{ "name": "Acme Agent", "contact_email": "[email protected]", "accepted_terms": true }' -
2 Verify the contact address
The key is live from the moment it is issued, but crippled: until you click the link we email, it reaches exactly three tools. Lost the mail?
POST /mcp/register/resendwith{"agent": "<slug>"}. -
3 Connect an MCP client
Streamable HTTP, stateless. Point any MCP-capable client — Claude, an SDK client, the MCP Inspector — at the endpoint with your key as a bearer token.
{ "mcpServers": { "myinfluenceai": { "type": "http", "url": "https://api.myinfluenceai.com/mcp", "headers": { "Authorization": "Bearer mi_live_<key_id>_<secret>" } } } } -
4 Find out what you hold
whoamianswers "what am I allowed to do" in one call — principal, scopes, rate limit, verification state and the brands this key can reach. Call it first whenever something is denied and you do not know why.platform_capabilitiesreturns the formats, pillars and caption platforms, so nothing has to be hardcoded. -
5 Analyse a website and pick a plan
website_analyzereads a public URL and proposes brand settings — niche, audience, tone, keywords.plan_listreturns the plans and how much they deliver per day. Both are read-only. -
6 Create the brand
brand_createwrites the brand row, grants it the MCP add-on and records the reseller consent — then stops. No research, no production, no spend. You must passaccepted_terms: truehaving actually read the linked documents on the brand's behalf. -
7 Send a human to checkout
checkout_createreturns a Stripe URL. An agent cannot pay. A person completes that checkout, Stripe calls us, and the brand activates. Until it does, every brand-scoped tool refuses withentitlement_required. -
8 Configure delivery and prove your verifier works
delivery_configuretakes your webhook URL and returns a signing secret once. Store it before you read the next line. Thendelivery_testsends a signed sample so you can confirm your HMAC check accepts a good signature — and, more importantly, rejects a bad one — before any real content depends on it. -
9 Receive the daily package
From then on, each produced item is POSTed to your endpoint as a
package.readyevent. If your endpoint was down,package_listandpackage_getreturn the same bytes, anddelivery_replayre-sends. -
10 Optional: let us publish it too
social_connect_linkmints a URL the brand's owner opens to connect their own TikTok, Instagram, YouTube and other accounts. You cannot do this step for them — authorising an account happens on the platform's own sign-in page. Once at least one account is connected they are offered publishing in Telegram, and if they accept we post approved content to those accounts as well as delivering it to you.social_accounts_listtells you where they got to.
Authentication
Keys look like mi_live_<key_id>_<secret> and go in an Authorization: Bearer header. The secret half is shown once, at creation, and is stored only as a peppered hash — we cannot recover it for you, so a lost key is replaced, not retrieved.
Self-serve registration always grants the same thing: the developer tier and the provision preset. It ignores any tier, scope or brand grant you ask for. A self-serve key can never carry publish:write, outreach:send or pii:read — those take an irreversible or privacy-bearing action on someone's behalf, so granting them is a separate, deliberate act.
tools/list is filtered to what your key can actually call. A tool you would only ever be denied is not advertised, because it would waste your context and your calls.
Before your email is verified
Registration is unauthenticated, so a contact address is an unproven claim until someone opens the link we send. An unverified key reaches only the tools listed below; everything else returns email_verification_required. The key is issued before verification and crippled, rather than minted on click, so a live credential never has to travel through an inbox.
| Tool | What it gives you |
|---|---|
plan_list | List plans |
platform_capabilities | Platform capabilities |
whoami | Who am I |
Scopes
A key carries an explicit, literal list of scopes. Presets are a shortcut at minting time only — the stored key always holds the expanded list, so widening a preset later can never widen a key that already exists.
| Scope | Grants |
|---|---|
brands:read | Read brand profiles, schedules and pipeline status. |
content:read | Read hooks, strategy, calendar and produced content. |
content:write | Create or edit hooks, strategy and captions. |
produce:write | Spend the brand’s quota to research or produce content. |
analytics:read | Read post metrics and pillar performance. |
publish:write never self-serve | Approve content for publication to connected social accounts. |
social:read | List connected social accounts (names and platforms only). |
billing:write | Create checkout sessions and change plans. |
outreach:read | Read cold-outreach campaigns and prospects. |
outreach:send never self-serve | Send cold outreach on the brand’s behalf. |
pii:read never self-serve | Read end-customer personal data (emails, names). |
delivery:write | Configure webhook delivery and replay past deliveries. |
Presets
readonly—brands:read,content:read,analytics:read,social:readdeliver—brands:read,content:read,delivery:writeprovision—brands:read,content:read,billing:write,delivery:write
Self-serve registration always grants provision, and the scopes marked above are never included in any preset and never implied by another scope.
Tool reference
All 20 tools, generated from the running registry. Brand-scoped tools take a brand slug and require both the MCP add-on and an active subscription on that brand; UUIDs are refused. Writes change state and are audited.
brand_activate
Activate a brand
Idempotent. Normally fired automatically by the Stripe webhook — call it only if payment completed but the brand is still inactive. Safe to retry.
Input schema
{
"type": "object",
"properties": {
"brand": {
"type": "string",
"description": "Brand slug."
}
},
"required": [
"brand"
],
"additionalProperties": false
}
brand_create
Create a brand
Create a brand owned by this agent. All work — publishing profile, strategy, first production — is DEFERRED until payment completes, so this call is cheap and reversible. Requires accepted_terms: you accept as a reseller on the brand’s behalf, and that assertion is recorded against your agent id. Follow with checkout_create.
Input schema
{
"type": "object",
"properties": {
"brand_name": {
"type": "string",
"maxLength": 120,
"description": "Display name. The slug is derived from it unless you pass one."
},
"slug": {
"type": "string",
"maxLength": 63,
"pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$",
"description": "Optional explicit slug."
},
"website": {
"type": "string",
"maxLength": 2000
},
"niche": {
"type": "string",
"maxLength": 500
},
"tone": {
"type": "string",
"maxLength": 200
},
"plan": {
"type": "string",
"maxLength": 40,
"description": "Intended plan id; used by checkout_create."
},
"owner_email": {
"type": "string",
"maxLength": 320,
"description": "Optional. The end brand’s email, for the claim invite and consent record."
},
"accepted_terms": {
"type": "boolean",
"description": "Must be true. You are asserting authority to accept on the brand’s behalf."
}
},
"required": [
"brand_name",
"accepted_terms"
],
"additionalProperties": false
}
brand_get
Get a brand
Full profile for one brand: niche, tone, goal, keywords, pillar ratios, palette, schedule and subscription state.
Input schema
{
"type": "object",
"properties": {
"brand": {
"type": "string",
"description": "Brand slug."
}
},
"required": [
"brand"
],
"additionalProperties": false
}
brand_list
List brands
Every brand this key can reach, with plan and subscription status.
Input schema
{
"type": "object",
"properties": {},
"additionalProperties": false
}
checkout_create
Create a checkout session
Return a Stripe Checkout URL for a brand. PASSTHROUGH billing: the end brand pays directly — hand them the URL. On payment our webhook activates the brand automatically.
Input schema
{
"type": "object",
"properties": {
"brand": {
"type": "string",
"description": "Brand slug."
},
"plan": {
"type": "string",
"maxLength": 40,
"description": "Plan id from plan_list. Defaults to the brand’s current plan."
},
"success_url": {
"type": "string",
"maxLength": 2000
},
"cancel_url": {
"type": "string",
"maxLength": 2000
}
},
"required": [
"brand"
],
"additionalProperties": false
}
delivery_configure
Configure delivery
Set or update where and how this brand’s daily content package is delivered. Returns the signing secret ONCE, on first configuration or when rotate_secret is true — store it, it cannot be read back. telegram_enabled is FALSE by default: an agent-provisioned brand is webhook-only unless you deliberately turn the Telegram approval card back on, in which case BOTH fire.
Input schema
{
"type": "object",
"properties": {
"brand": {
"type": "string",
"description": "Brand slug."
},
"webhook_url": {
"type": "string",
"maxLength": 2000,
"description": "Public https:// endpoint. Private and loopback addresses are refused."
},
"mode": {
"type": "string",
"enum": [
"deliver_only",
"publish_through"
],
"description": "deliver_only (the default) hands the finished package to your webhook and you publish it. publish_through means we post it to the accounts the brand connected — it is READ-ONLY here: only the brand owner can grant it, in Telegram, after connecting an account. Start that with social_connect_link and watch for it with social_accounts_list."
},
"auto_approve": {
"type": "boolean",
"description": "Publish-through only, and a kill switch rather than a schedule: false stops unattended publishing for this brand even if the owner turned it on. Whether and WHEN it happens stays theirs. Default false."
},
"platforms": {
"type": "array",
"description": "Caption variants to include. Omit for all 11."
},
"telegram_enabled": {
"type": "boolean",
"description": "Also send the Telegram approval card. Default false."
},
"approval_mode": {
"type": "string",
"enum": [
"none",
"notify",
"gate"
],
"description": "What a human tap on that card means. Requires telegram_enabled. \"gate\" holds the package until a human approves in Telegram — you get package.pending_approval first and package.ready only after the tap. \"notify\" delivers immediately and the card is a receipt. Default \"none\"."
},
"paused": {
"type": "boolean",
"description": "Stop delivering without losing configuration."
},
"rotate_secret": {
"type": "boolean",
"description": "Mint a new signing secret. Breaks existing verification until you redeploy."
}
},
"required": [
"brand"
],
"additionalProperties": false
}
delivery_replay
Replay a delivery
Re-queue a past delivery. KEEPS THE ORIGINAL idempotency key, so if the first attempt did reach you, your own deduplication recognises the duplicate and drops it.
Input schema
{
"type": "object",
"properties": {
"brand": {
"type": "string",
"description": "Brand slug."
},
"delivery_id": {
"type": "string",
"maxLength": 40,
"description": "From package_list."
}
},
"required": [
"brand",
"delivery_id"
],
"additionalProperties": false
}
delivery_test
Send a test delivery
Queue a signed sample payload to the configured webhook so you can verify your HMAC check before real content depends on it. Verify X-MI-Signature as t=<unix>,v1=<hex hmac-sha256 of "<t>.<raw body>"> using your signing secret, with 5 minutes of clock tolerance.
Input schema
{
"type": "object",
"properties": {
"brand": {
"type": "string",
"description": "Brand slug."
}
},
"required": [
"brand"
],
"additionalProperties": false
}
job_get
Get a job
Status of one queued job by id. Scoped to the brand, so a job id from another tenant is simply not found.
Input schema
{
"type": "object",
"properties": {
"brand": {
"type": "string",
"description": "Brand slug."
},
"job_id": {
"type": "integer",
"description": "Job id returned by a mutating tool."
}
},
"required": [
"brand",
"job_id"
],
"additionalProperties": false
}
package_get
Get a content package
The full content package for one request_id — media URLs, all caption variants, and the music credit. Returns exactly what was PUSHED if it was delivered, so a pull and a push can never disagree. Works even for content produced before delivery was configured.
Input schema
{
"type": "object",
"properties": {
"brand": {
"type": "string",
"description": "Brand slug."
},
"request_id": {
"type": "string",
"maxLength": 120,
"description": "From a package.ready payload or pipeline_status."
}
},
"required": [
"brand",
"request_id"
],
"additionalProperties": false
}
package_list
List deliveries
Delivery attempts for a brand, newest first. THE CATCH-UP TOOL: after an outage, list with status "failed" or "dead" and replay what you missed.
Input schema
{
"type": "object",
"properties": {
"brand": {
"type": "string",
"description": "Brand slug."
},
"status": {
"type": "string",
"enum": [
"pending",
"sending",
"delivered",
"failed",
"dead"
]
},
"since": {
"type": "string",
"maxLength": 40,
"description": "ISO 8601 timestamp."
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"description": "Default 20."
},
"cursor": {
"type": "string",
"maxLength": 40,
"description": "next_cursor from a previous page."
}
},
"required": [
"brand"
],
"additionalProperties": false
}
pipeline_status
Pipeline status
Live production state for a brand: queued and recent jobs, video counts by status, unused hooks (the real runway figure), and the five most recent items.
Input schema
{
"type": "object",
"properties": {
"brand": {
"type": "string",
"description": "Brand slug."
}
},
"required": [
"brand"
],
"additionalProperties": false
}
plan_list
List plans
Available subscription plans with price and the DAILY DELIVERABLE VOLUME each one buys (videos_per_day, carousels_per_day, posts_per_day) — the numbers that decide how many packages a webhook will receive.
Input schema
{
"type": "object",
"properties": {},
"additionalProperties": false
}
platform_capabilities
Platform capabilities
What this platform can produce: content formats, content pillars, the caption platforms generated for every post, video statuses, and the available scopes. Read this once at integration time instead of hardcoding enums.
Input schema
{
"type": "object",
"properties": {},
"additionalProperties": false
}
social_accounts_list
List connected social accounts
The social accounts this brand has connected, and whether we publish to them. publish_mode is "deliver_only" when we hand you the package and you publish it, or "publish_through" when the brand owner has asked us to post it for them. Account IDs are never returned — this answers "did my user finish connecting", not "who are they".
Input schema
{
"type": "object",
"properties": {
"brand": {
"type": "string",
"description": "Brand slug."
}
},
"required": [
"brand"
],
"additionalProperties": false
}
social_connect_link
Create a link to connect social accounts
Mint a one-time URL the brand owner opens to connect their own social accounts — TikTok, Instagram, YouTube, LinkedIn and the rest. Hand it to your user; they sign in on each platform's own page and we never see their password. Once at least one account is connected they are offered publishing, and if they accept we post the daily content for them instead of only sending it to your webhook. You cannot do this step for them: authorising an account is something only its owner can do. Watch for the result with social_accounts_list.
Input schema
{
"type": "object",
"properties": {
"brand": {
"type": "string",
"description": "Brand slug."
},
"ttl_minutes": {
"type": "integer",
"minimum": 1,
"maximum": 10080,
"description": "How long the link stays valid. Default 60, maximum 7 days."
}
},
"required": [
"brand"
],
"additionalProperties": false
}
subscription_status
Subscription status
Plan, status, renewal date and add-ons for a brand. Check this before assuming packages will keep arriving — delivery stops when a subscription is cancelled.
Input schema
{
"type": "object",
"properties": {
"brand": {
"type": "string",
"description": "Brand slug."
}
},
"required": [
"brand"
],
"additionalProperties": false
}
telegram_link_create
Create a Telegram approval link
Mint a single-use link that connects a human's Telegram chat to this brand, so they can review and approve content in chat. Hand the link to your user; when they open it the chat is bound and they get the full review experience — daily cards, regeneration, feedback and scheduling commands. Pair with delivery_configure approval_mode "gate" to require their approval before content is delivered to you. The link is single-use and expires; mint another if it goes unused.
Input schema
{
"type": "object",
"properties": {
"brand": {
"type": "string",
"description": "Brand slug."
},
"ttl_minutes": {
"type": "integer",
"minimum": 1,
"maximum": 10080,
"description": "How long the link stays valid. Default 60, maximum 7 days."
}
},
"required": [
"brand"
],
"additionalProperties": false
}
website_analyze
Analyze a website
Read a public website and return suggested brand settings (name, industry, tone, goal, audience, keywords) plus scraped brand colours. Step one of onboarding: feed the result into brand_create. Results are cached, so re-analysing the same URL is free.
Input schema
{
"type": "object",
"properties": {
"website": {
"type": "string",
"maxLength": 2000,
"description": "Public https:// URL of the brand’s site."
}
},
"required": [
"website"
],
"additionalProperties": false
}
whoami
Who am I
Identify the calling key: principal, scopes, rate limit, and the brands it can reach. Call this first when debugging a permission problem — it answers "what am I allowed to do" without guessing.
Input schema
{
"type": "object",
"properties": {},
"additionalProperties": false
}
Errors
A tool failure comes back as a normal result marked isError, not a protocol fault, so your agent can read it and correct itself. Every error carries a code, a message and a next_step written for a program rather than a person.
| Code | Meaning | Next step |
|---|---|---|
auth_required | Missing or malformed Authorization header. | Send `Authorization: Bearer mi_live_<key_id>_<secret>`. No key yet? POST /mcp/register, or read https://agents.myinfluenceai.com/developers/mcp/. |
key_invalid | That API key is not valid. | Check the key was copied whole. Secrets are shown once at creation and cannot be recovered. See https://agents.myinfluenceai.com/developers/mcp/. |
key_revoked | That API key has been revoked. | Mint a new key from the developer console. |
key_expired | That API key has expired. | Mint a new key from the developer console. |
agent_disabled | This agent account is disabled. | Contact [email protected]. |
email_verification_required | Confirm your contact email before using this tool. | Open the link we emailed when you registered. Lost it? POST /mcp/register/resend {"agent":"<slug>"}. See https://agents.myinfluenceai.com/developers/mcp/. |
scope_required | This key does not carry the scope required for that call. | Mint a key with the scope listed in `required_scope`. |
brand_not_found | No brand with that identifier is available to this key. | Call `brand_list` to see the brands this key can reach. |
brand_not_authorized | No brand with that identifier is available to this key. | Call `brand_list` to see the brands this key can reach. |
entitlement_required | The MCP add-on is not enabled for this brand. | Complete checkout for this brand, or contact support to enable the add-on. |
rate_limited | Too many calls. Slow down. | Wait `retry_after_seconds` and retry. Back off exponentially on repeats. |
quota_exceeded | This brand has reached its plan limit for that action. | Upgrade the plan with `plan_change`, or wait for the limit window to roll. |
quota_unavailable | Usage limits are temporarily unavailable, so the call was refused rather than allowed. | Retry in a minute. This is a transient server condition, not a limit you hit. |
invalid_input | One or more arguments were rejected. | Read `details` for the offending field, fix it, and retry. |
unsafe_url | That URL points somewhere we will not fetch or deliver to. | Supply a public https:// URL. Private, loopback and link-local addresses are refused. |
consent_required | Terms must be accepted before a brand can be created. | Read the documents in `legal_urls`, then retry with `accepted_terms: true`. |
not_found | No such record. | Check the identifier. List endpoints return the ids this key can use. |
conflict | That action conflicts with the current state. | Read `state` and retry only if it makes sense to. |
not_configured | Delivery is not configured for this brand. | Call `delivery_configure` with a webhook_url first. |
internal_error | Something went wrong on our side. | Retry once. If it persists, contact [email protected] with the `request_id`. |
brand_not_found and brand_not_authorized return the same message on purpose: a key must not be able to discover which brands exist by probing.
Webhook delivery
Every delivery carries an X-MI-Signature header in the form t=<unix>,v1=<hex>, where v1 is an HMAC-SHA256 of the literal string "<t>.<raw body>" under your signing secret. The timestamp is inside the signed value, so a captured body cannot be replayed with a fresh timestamp.
Three things a correct verifier does, and a plausible one does not: hash the raw request body (not a re-serialised object — key order changes the bytes), compare with a constant-time comparison, and reject anything outside a ±5 minute window. A verifier that skips the timestamp check silently accepts forged calls forever.
We accept any 2xx as delivered. Anything else retries on a fixed schedule and then stops. Respond fast and process asynchronously: a slow endpoint is indistinguishable from a broken one.
Headers
POST /your/webhook HTTP/1.1
Content-Type: application/json
X-MI-Event: package.ready
X-MI-Signature: t=1756512000,v1=9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
Verify it — Node
import crypto from 'crypto';
// body must be the RAW request bytes, not a re-serialised object:
// JSON.stringify(JSON.parse(body)) can reorder keys and change the hash.
export function verify(rawBody, header, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(
String(header || '').split(',').map(kv => kv.split('=').map(s => s.trim())),
);
const t = parseInt(parts.t, 10);
if (!Number.isFinite(t)) return false;
// Without this, a captured body replays forever.
if (Math.abs(Math.floor(Date.now() / 1000) - t) > toleranceSeconds) return false;
const expected = crypto.createHmac('sha256', secret)
.update(`${t}.${rawBody}`, 'utf8').digest('hex');
const a = Buffer.from(expected, 'hex');
const b = Buffer.from(String(parts.v1 || ''), 'hex');
if (a.length !== b.length || a.length === 0) return false;
return crypto.timingSafeEqual(a, b);
}
Verify it — Python
import hashlib, hmac, time
def verify(raw_body: bytes, header: str, secret: str, tolerance_seconds: int = 300) -> bool:
parts = dict(
kv.strip().split("=", 1) for kv in (header or "").split(",") if "=" in kv
)
try:
t = int(parts["t"])
except (KeyError, ValueError):
return False
# Without this, a captured body replays forever.
if abs(int(time.time()) - t) > tolerance_seconds:
return False
signed = f"{t}.".encode() + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))
Retries
A non-2xx response (or a timeout) is retried 6 times in total, then marked dead:
1m → 5m → 30m → 2h → 6h → 24h → dead
That is roughly 33 hours of tolerance for an endpoint that is down. A dead delivery is not lost — package_list still returns it and delivery_replay re-sends it.
The daily package
One frozen shape, assembled once and used identically by the webhook push and the package_get pull — so a consumer catching up after an outage gets exactly what it normally receives.
The one thing consumers get wrong: media.kind is "video" or "carousel". For a carousel, video_url is null and the ordered slide URLs are in media.slides. Branch on kind; a downloader that assumes a single URL breaks on the first carousel day.
Music credit is not optional. When music.credit_required is true, the ready-made music.credit_line must appear in the caption you publish — not in file metadata. Instagram, TikTok and YouTube all re-encode uploads and strip container tags, so metadata attribution is not attribution. Nearly every track in the library is CC BY.
Media URLs expire. expires_at is on every package: fetch and store the bytes, do not hotlink.
Captions
A variant is generated for all 11 platforms on every post, regardless of what the brand has connected, so a deliver-only consumer can publish anywhere: linkedin, tiktok, x, reddit, instagram, facebook, youtube, bluesky, pinterest, threads, mastodon. Narrow the set with the platforms argument to delivery_configure. A platform with no stored caption is absent rather than null, so "not generated" is distinguishable from "generated empty".
Shape
A real payload, assembled by the same code that builds yours. This one was configured for three platforms so the sample stays readable; by default captions holds all 11.
{
"package_version": "1",
"brand": "acme-dental",
"request_id": "req_9f4c21",
"date": "2026-08-30",
"format": "talking_points",
"pillar": "educational",
"hook": "Three things every new patient asks — answered in 30 seconds.",
"media": {
"kind": "video",
"video_url": "https://cdn.my-influence-ai.com/v/req_9f4c21.mp4",
"slides": null,
"thumbnail_url": "https://cdn.my-influence-ai.com/v/req_9f4c21.jpg",
"duration_seconds": 28
},
"captions": {
"linkedin": {
"text": "Patients ask the same three questions before every first appointment. Here is how we answer them.",
"hashtags": []
},
"tiktok": {
"text": "The 3 questions we get every single day",
"hashtags": [
"#dentaltok",
"#patienttips"
]
},
"instagram": {
"text": "Three things every new patient asks. Save this for later 🦷",
"hashtags": [
"#dentalcare",
"#patienttips"
]
}
},
"default_caption": "Three things every new patient asks. Save this for later.",
"music": {
"title": "Sunlit",
"artist": "Kirkoid",
"license": "CC BY 3.0",
"license_url": "https://creativecommons.org/licenses/by/3.0/",
"credit_required": true,
"credit_line": "🎵 Music: \"Sunlit\" by Kirkoid (ccMixter, CC BY 3.0)"
},
"expires_at": "2026-09-29T06:00:00.000Z"
}
Rate limits and quotas
Rate limits are per key and reported by whoami; a rate_limited error carries retry_after_seconds. Back off exponentially — a retry loop against a limit is the fastest way to get a key disabled.
Plan quotas are enforced per brand and fail closed: if we cannot determine your remaining quota, the call is refused rather than allowed. That is deliberate for an automated caller, and it is why quota_unavailable is a distinct code from quota_exceeded — the first is transient and worth retrying, the second is not.
Every response carries a request_id. Quote it when you contact support; it is how we find your call.
Versioning
The tool schemas and the package schema are frozen contracts, checked against a fixture in CI — an accidental change to either fails our build, not yours.
Additive fields do not bump a version. A breaking change mounts a new path (/mcp/v2); /mcp never silently becomes v2. Deprecations run 90 days, are flagged on the affected responses, and are emailed to the owners of affected keys.
Machine-readable discovery lives at GET https://api.myinfluenceai.com/mcp/info and https://agents.myinfluenceai.com/.well-known/mcp/server.json; both are unauthenticated and always describe the deployed server.
Terms
Creating a brand means accepting these on that brand's behalf. Read them before you pass accepted_terms: true.
Frequently asked questions
Does Agents by MyInfluence AI have an MCP server?
Yes. It is hosted at https://api.myinfluenceai.com/mcp, speaks Streamable HTTP, and authenticates with a bearer API key. Registration is self-serve at POST /mcp/register and returns a key immediately. There is nothing to install and no local process to run.
How does an AI agent subscribe programmatically?
Register for a key, verify the contact email, then call website_analyze, plan_list and brand_create to provision a brand, and checkout_create to get a Stripe URL. A human completes that checkout — an agent cannot pay on its own. Once Stripe confirms, the brand activates and daily delivery begins.
What is in a daily content package?
A rendered 9:16 short-form video (or an ordered set of carousel slides), a thumbnail, the hook and content pillar, a caption variant for each of 11 platforms, a default caption, and a music block with a ready-made credit line that must be published in the caption. Media URLs carry an explicit expiry.
How do I verify the webhook signature?
Compute an HMAC-SHA256 over the string "<timestamp>.<raw request body>" using your signing secret, and compare it in constant time to the v1 value in the X-MI-Signature header. Reject any request whose t is more than five minutes from now. Prove it works with delivery_test before real content depends on it.
What does it cost?
The same plans humans buy: from $149/month per brand, and the brand's human owner completes the checkout. The default agent interface (developer tier) adds nothing on top — no signup fee, no per-call charge. There is one paid tier: MCP Partner at $499/month, for higher rate limits, many more brands and a white-label server name — contact support for it. Everything documented here works on the free developer tier.
Can an agent publish to social accounts through the API?
We can publish, but you cannot make us. Call social_connect_link and hand the URL to the brand's owner; they sign in at each platform themselves, because authorising an account is something only its owner can do. If they then turn publishing on, we post approved content to those accounts. Every post is still approved by that person, and no API key — self-serve or otherwise — can grant itself the publish:write scope or flip a brand into publish-through.
Can an agent create brands for other people's businesses?
Yes, and that is the intended reseller pattern — but brand_create records who accepted the terms and on whose behalf, and the human who pays is the one who owns the account. Creating brands without a real customer behind them just creates rows that never activate.