Build on Influensia
Everything you can do in the Influensia app you can do over the API: browse verified link and channel inventory, vet sellers, propose escrowed orders, follow fulfilment, and monitor every purchased link. The web app is just one client — this guide is for the other kind: your scripts, your integrations, and AI agents working on your behalf.
Overview
There are two surfaces, backed by the same service layer and the same permissions — an agent can never do more through MCP than its owner can do in the app:
- REST API — resource-oriented, for your own code. The OpenAPI document is the contract of record; every request and response shape, with examples, lives there.
- MCP server — task-level tools for AI agents over the Model Context Protocol (Streamable HTTP). One tool call does what would take an agent five REST round-trips.
https://influensia.com/api/v1/openapi.jsonInteractive referencehttps://influensia.com/docsMCP endpointhttps://app.influensia.com/api/mcp/mcpllms.txthttps://influensia.com/llms.txtPointing an agent at Influensia? /llms.txt is the one-page orientation written for it — what the platform is, how to authenticate, and a numbered “start here” path. It is kept in lock-step with the tool registry by a test, so it never lies about what exists.
Authentication
Create an API key in Settings → API keys. Keys are prefixed flu_, shown once, and hashed at rest. A key belongs to you and one workspace: everything an agent does with it lands in the audit log with a human owner attached, and a buy-side key cannot act in your sell-side workspace (or vice versa) — switch workspace, create a key there.
| Surface | Send the credential as |
|---|---|
REST (/api/v1) | x-api-key: flu_… |
MCP (/api/mcp/mcp) | Authorization: Bearer flu_… |
| MCP clients that speak OAuth | No key needed — the server supports MCP OAuth with dynamic client registration; you consent in your normal Influensia session |
- Workspace selection — pass
X-Workspace: <id|slug>on any REST call; otherwise the key's workspace applies. - Hygiene — treat keys like passwords: server-side only, never in a browser bundle or a repo. Per-key expiry is available; revoke from the same settings panel.
Quickstart
1 · Confirm the credential
Always start with GET /api/v1/me (MCP: whoami) — it returns who you are, the active workspace, its kind (buy-side client or sell-side provider) and your role in it. Everything else depends on that context.
The public half of that identity is GET /api/v1/me/profile (MCP: get_my_profile) — the same shape as GET /api/v1/users/{handle} but addressed by credential, so it answers before a handle has been claimed. Write it back with PATCH /api/v1/me/profile (MCP: update_my_profile): headline, location, bio, services, niches, handle and colour mode, any subset per call. Read before you write — a member edits the same fields in their settings, and a patch is a replace, not a merge.
curl https://influensia.com/api/v1/me \
-H "x-api-key: flu_YOUR_KEY"2 · Find inventory
The canonical domain registry answers “who sells a link on this site, at what price, on what terms” — same domain from many providers, one comparison, cheapest first. Each offer carries that seller's own content terms (per-word rate, word tiers, writing languages).
curl "https://influensia.com/api/v1/domains/example-magazine.com" \
-H "x-api-key: flu_YOUR_KEY"Metrics belong to the domain, not to the offer. dr, da, traffic, refDomains, spamScore and organicKeywordsare identical on every offer for one site: they are read from the shared registry whenever any seller's connected data provider has checked it, and from the selling seller's own claim only while nobody has. verified tells you which, verifiedBy names the providers and metricsCheckedAt says when they last read it. Two fields sit outside that rule, in opposite directions. trafficValue has no verified counterpart anywhere, so treat it as a claim. trend has no CLAIMED counterpart: +1 rising or -1 declining is derived from two of our own monthly readings of the site, and it is null — the common case — whenever we have not measured a direction ourselves. Nobody can state one, so a null there means unmeasured, never flat. Compare offers on price, delivery, link terms and content terms; comparing their metrics is comparing a site to itself.
Broader discovery: GET /api/v1/domains is the faceted directory search, GET /api/v1/markets browses seller storefronts, and GET /api/v1/channels lists verified social channels sold alongside links. Filters and facet counts are documented per-endpoint in the API reference.
Vetting the people behind the inventory: GET /api/v1/users/{handle} is the full member profile, and GET /api/v1/users/verified?ids=… is the identity question alone, batched — a map of user id to boolean for up to 100 ids, where ids that don't resolve read false rather than erroring. It answers for the person. The workspace-level Verified business badge is a different fact and travels on market and offer payloads as sellerVerified — on the LIST shapes too, so a browse can be filtered on it without a detail call per market.
A market's policies array is two lists spliced together. The platform's own chips come first and are computed from the inventory on every read, so they cannot outlive the fact behind them: 12-month link guarantee appears only when every listing currently on sale is sold for 12 months or longer (a permanent link, sent as durationMonths: null, counts). One shorter listing and the chip is gone. Everything after them is the seller's own prose — "No PBNs", "Disclosure compliant" — which nothing checks. A seller cannot mint a platform chip by typing its wording: the label is stripped from their text before the derived list is prepended. Report the two kinds differently.
3 · Propose an order
Every link placement is created with its brief — the anchor text and target page are the agreement the platform later verifies the published link against. Briefs are keyed by listing id; targetPage may be omitted only when campaignId names a campaign with a site (the homepage becomes the target). The brief also names the placementNiche — default standard; a priced vertical from the listing’s own nichePrices(crypto, casino, …) re-prices the item from that variant, and a vertical the listing doesn’t accept refuses the order. The item snapshots the listing’s dofollow/sponsored/duration terms at creation. Always send an Idempotency-Key so retries are safe.
curl -X POST https://influensia.com/api/v1/orders \
-H "x-api-key: flu_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 0d6f2c9a-first-order" \
-d '{
"marketId": "MARKET_ID",
"domainIds": ["LISTING_ID"],
"campaignId": "CAMPAIGN_ID",
"briefs": {
"LISTING_ID": {
"anchor": "best crm for startups",
"targetPage": "https://yoursite.com/crm",
"notes": "Mention the free tier; no competitor comparisons."
}
}
}'The response is the created order with status requested — a proposal, not a purchase. A human funds escrow through checkout in the app; work starts only then. Poll GET /api/v1/orders/{id} (MCP: get_order_status) to follow per-placement fulfilment — each item moves queued → writing → review → publishing → live, and escrow releases per link.
REST conventions
These hold on every /api/v1 endpoint, so client code can be written once.
Errors — RFC 9457 problem+json
Every error is application/problem+json: a stable type URI, a human title, and a detail that says what to change, not just what failed. Validation errors add a field-level errors map. A 429 always carries Retry-After (seconds):
HTTP/1.1 429 Too Many Requests
Retry-After: 41
Content-Type: application/problem+json
{
"type": "https://influensia.com/problems/rate-limited",
"title": "Rate limit exceeded.",
"status": 429,
"detail": "Limit is 240 requests per 60s. Retry after 41s."
}Retry guidance: honor Retry-After on 429; back off and retry on 5xx; treat every other 4xx as yours to fix — repeating the request unchanged will not help.
Idempotency
Every POST accepts an Idempotency-Key header (any unique string, 24h window). The same key with the same body replays the stored response with Idempotency-Replayed: true; the same key with a different body is a 409. Only sub-500 responses are stored, so failed requests stay retryable. For anything that creates an order, sends a message, or moves state: always send one.
Pagination
Cursor-based everywhere: ?cursor=&limit= (default 25, max 100), response { items, nextCursor }. Ids are time-ordered uuidv7, so cursors sort chronologically. There is no offset pagination.
GET /api/v1/orders?limit=25
→ { "items": [ /* 25 orders */ ], "nextCursor": "0198c5d2-…" }
GET /api/v1/orders?limit=25&cursor=0198c5d2-…
→ { "items": [ /* next page */ ], "nextCursor": null }Rate limits
240 requests per minute per credential, in a fixed window. Budget for bursts, honor Retry-After, and prefer the task-level MCP tools when driving an agent — one tool call replaces several REST calls.
Plan allowances
Almost nothing here is metered — listings, orders, messages and search are unlimited on every plan, because the platform fee is the business model. Projects are the exception. A project is one website, and registering one buys that domain’s authority and backlink baseline from a paid vendor on our account, refreshed monthly for as long as the project lives. So the plan caps them: Free tracks 1 website, Pro tracks 10.
POST /api/v1/projects answers 409 once the allowance is spent. Two details matter for a client that has to handle it. Archived projects count — they still hold their domain, so archiving frees nothing. And on Free, DELETE /api/v1/projects/{id} always reports {"outcome": "archived"} rather than erasing, which means no sequence of calls returns a slot. The way to track a different domain on a full workspace is PATCH /api/v1/projects/{id} with a new site, accepted until that project has campaigns and costing nothing; otherwise the workspace owner upgrades in Settings → Billing.
Versioning
/v1 is additive-only: fields and endpoints are added, never removed or re-typed. A breaking change would ship as /v2 beside /v1 with a documented sunset of at least six months. Write clients that tolerate unknown fields.
Writes return the resource
Mutations return the changed resource, not {ok: true} — chain on the returned ids instead of re-fetching.
The MCP server
https://app.influensia.com/api/mcp/mcp — Model Context Protocol over Streamable HTTP, stateless: every tool call is self-contained, so serverless and multi-session clients work without ceremony. The design principles, because they shape how your agent should use it:
- Task-level, not CRUD.
request_ordertakes the market, the listings and the briefs in one call. If your agent is chaining five reads to do one job, look for the tool that does the job. - Human-meaningful identifiers.
get_marketaccepts a slug,compare_domain_offersa domain name,get_member_profilea handle — tools resolve to internal ids themselves. - Errors are the recovery path. When an input is wrong, the error text says what to call instead (for example,
request_orderlists the listings it did not recognise and points atget_market). - Unknown is never bad news. Absent metrics mean “not measured”, a
pendinglink check means “not yet checked”, a null completion rate means “new seller”. Reporting unknowns as failures is the one mistake the tool descriptions warn about hardest.
redeem_reward spends a no-refund balance, tip_post moves credits to another member with no undo, refresh_domain_metrics spends the seller's own third-party API budget), the rule is: call them only when the user explicitly asked, never speculatively.Tool catalog
108 tools. The roster you are SERVED depends on your workspace: a buying workspace gets the buy-side tools, a selling one the sell-side, and the shared tools appear in both. read tools are always safe; write tools change state the user can undo in the app; confirm tools are externally visible or cost-bearing (see the note above).
Account Who am I, what plan, what balance. Money never moves through MCP.
whoami | read | Identity, active workspace, role and every membership. Call this first. |
get_billing_summary | read | Plan, billing interval (monthly or yearly), renewal and seats — read-only. |
list_billing_history | read | Order payments and subscription invoices, with receipt / invoice PDF links. |
get_rewards_summary | read | Credit balance, badges and quest standing, reconciled on read. |
redeem_reward | confirm | Spend credits on pro_month, fee_voucher or market_boost. No refunds — only on an explicit user ask. |
tip_post | confirm | Tip credits to a feed post's author — capped per day, no undo. Only on an explicit user ask. |
get_my_profile | read | Your own profile, by credential rather than handle — so it answers before a handle exists. Read it before writing it. |
update_my_profile | write | Name, handle, headline, location, bio, services, niches, colour mode. Each field replaces, never merges. |
get_notification_prefs | read | Which kinds are on, and the digest frequency. |
update_notification_prefs | write | Switch kinds on or off. Security notices ignore them by design. |
tip_comment | confirm | Same for a comment's author — one shared daily budget with tip_post. |
Buying Find inventory, vet sellers, propose orders. Funding stays human.
get_market | read | One market with inventory, seller trust stats, content terms and policy chips (derived first, seller prose after). Accepts a slug. |
find_domains | read | Faceted directory search — DR/DA/traffic/spam-score/referring-domain filters, counts and facet suggestions. |
compare_domain_offers | read | Every seller's offer on one domain name, cheapest first. Takes the bare name. |
find_channels | read | Browse verified social channels (YouTube, Instagram, TikTok, X) with priced formats. |
get_channel | read | Every seller's offer on one channel — identity is (platform, handle). |
get_member_profile | read | Vet a member by handle: verification, live stats, reviews. |
request_order | confirm | Propose an order — link and channel placements, briefs inline. Creates a PENDING request; a human funds escrow. |
get_order_status | read | One order with per-item lifecycle and escrow state. |
list_orders | read | Orders with status and date filters. |
list_order_items | read | The flat incomplete-work queue across all orders. |
cancel_order | confirm | Un-propose a pending order before money moves. |
advance_order_item | confirm | Move a placement through its lifecycle. mark_live is the claim escrow acts on — releasing is still human-only. |
set_order_item_brief | confirm | Anchor, target page and content guidance for a placement. Freezes when the link goes live. |
get_basket | read | The workspace's shared basket — the multi-seller path. Paying stays human. |
add_basket_item | write | Add a link or channel listing, pinned to a campaign on the line. |
set_basket_item_brief | write | Anchor, target and content mode — checkout refuses an un-briefed link line. |
set_basket_campaign | write | Pin every line to one campaign at once. |
remove_basket_item | write | Take one line out. It's a shared basket — the line may be a teammate's. |
clear_basket | confirm | Empty it completely, teammates' lines included. No undo. |
list_reviewable_orders | read | Released orders still owed a review. |
write_review | confirm | Public and permanent, one per order — no edit, no delete. |
set_auto_release | confirm | The buyer's auto-release policy: verified live links release their own escrow. |
Selling Fulfilment and data quality for provider workspaces.
draft_order_item | confirm | Start a placement and write its draft document in one move. |
list_my_markets | read | Your own storefronts, drafts and archived included — the only view that is not live-only. |
list_market_inventory | read | One market’s listings, filtered and paged — the ids update_listing takes. |
add_listing | write | Sign one more domain: price, placement, the claimed metrics (DR, DA, traffic, referring domains, spam score, keywords, traffic value), content and link terms (dofollow cap included), niche variants, buyer-facing and internal notes. |
update_listing | write | Re-price, re-term, correct a stale DR/DA/traffic claim, or pause a listing. Nothing deletes a listing, anywhere. |
update_market | write | Rename, rewrite the description or the storefront/internal notes, relabel the currency, or unpublish back to a private draft. |
publish_market | confirm | Puts the storefront on sale. Refused until it has at least one listing. |
import_inventory | write | Start an import from a Google Sheet link — how a market is created. Files upload in the web app only. |
get_import | read | Poll an import: status, detected columns, guessed mapping, row counts, warnings. |
list_imports | read | Every import, so an agent can pick up one someone else started. |
list_import_rows | read | The parsed rows with their flags and issues, before they become priced offers. |
update_import_row | write | Correct or exclude one parsed row. |
confirm_import_mapping | write | Say which column is which and re-normalize every row. |
commit_import | write | Accepted rows become a new draft market — in the file’s detected currency, or converted to USD at today’s rate — or append to one you own. `contentDefaults` sets the writing terms for every domain at once, `placementDefault` says what the prices buy where the file is silent. |
reply_to_review | confirm | The seller's public answer — once, never editable. |
resync_market | write | Re-read the Google Sheet behind a market; the report arrives as a notification. |
list_my_channels | read | Your claimed channels: verification state, bio token, checks left, audience. |
claim_channel | write | Claim a handle and mint the one-time bio code a human then pastes in. |
check_channel_claim | confirm | Runs the bio-code check. Rationed — six failures close verification for that channel for good. |
refresh_channel | confirm | Buys a fresh read of a channel's public numbers. Once a day per channel, platform budget. |
archive_channel | confirm | Archives a channel and takes its placements off sale. Reversible; 409 while an order is live. |
restore_channel | write | Back on the roster, checks intact — placements stay paused until you price them again. |
list_channel_offers | read | A market's channel listings — the ids the offer tools take. |
add_channel_offer | write | Price one format on a verified channel. Unverified handles cannot be listed. |
update_channel_offer | write | Re-price, re-term or pause a placement. Unpausing re-checks the ownership claim. |
remove_channel_offer | confirm | Deletes an untraded channel placement. 409 once anything ordered it — pause instead. |
preview_channel_roster | read | Dry-run a roster sheet into rows with issues. Stores nothing. |
commit_channel_roster | confirm | Files the claims and prices the formats — verified ones go live at once. |
remove_market | confirm | Erases an unpublished market nothing has traded against; archives it in every other case, and says which it did. |
restore_market | write | Brings an archived market back as a draft — never straight onto the storefront. |
refresh_domain_metrics | confirm | Re-pull Ahrefs / Moz / Majestic numbers with the seller's own keys — spends their API budget (~150 Ahrefs units per domain). |
get_metrics_status | read | Which providers are connected and how fresh each domain is. |
Messaging The deal thread. Priced offers stay human-composed.
list_conversations | read | Threads with unread state. |
read_conversation | read | The messages in one thread, with shared markets and offers. Read before replying. |
open_conversation | confirm | Open (or reuse) a thread with a market's seller. |
send_message | confirm | Send a message — body plus an optional market reference. Deliberately no offers or files. |
Work Projects, campaigns, content and the demand board.
list_projects | read | Websites and their portfolio rollup — one project per registrable domain. |
get_project_health | read | Which websites need attention and WHY, ranked worst first. |
create_project | write | Register a website as a project. 409 once the plan allowance is spent — Free 1, Pro 10, archived included. |
update_project | write | Rename, change status, archive, or re-point the domain while it has no campaigns. |
remove_project | confirm | Erases a website nothing has ordered against; archives it otherwise (always, on Free), and says which. |
list_campaigns | read | Campaigns, filterable by project and status. |
create_campaign | write | Start a campaign inside a website, guardrails and all. |
update_campaign | write | Goals, guardrails, status. |
remove_campaign | confirm | Same two outcomes as remove_project — the board and its orders are kept either way. |
get_campaign_report | read | One campaign's numbers: placements, spend, links live. |
get_campaign_insights | read | AI reading of one campaign (Pro) — every insight cites a measured signal. |
recommend_placements | read | "What should this campaign buy next", from its own guardrails. Prefer it over find_domains when a campaign is in play. |
add_campaign_placements | write | Shortlist domains as prospects — no money, no order. |
create_document | write | Draft a document inside a campaign. |
list_documents | read | A website's documents — titles and status, no bodies. |
get_document | read | One document with its body — including an order item’s draft. |
update_document | write | Rewrite title, status or the whole body. No append — send the full text. |
list_feed | read | Read the community feed create_feed_post writes into. |
publish_article | confirm | An article under the user's byline. Drafts by default; a human decides whether it goes out. |
create_feed_post | confirm | Post to the community feed. |
list_my_articles | read | Your articles, drafts included. |
get_article | read | One article by slug, with its body. |
update_article | write | Revise title, kicker, format or the whole body. |
unpublish_article | confirm | Back to a private draft; republishing keeps the same URL. |
list_briefs | read | The demand board — open briefs; sellers pass matchingMyInventory to see what their live listings could fill. |
publish_brief | confirm | Publish what a campaign needs; sellers answer with priced offers. Answering has no tool on purpose. |
Team & access Who is in the workspace, and what guests can reach. Grants only matter for guests.
list_workspace_members | read | Everyone, with role and presence. |
list_invitations | read | Pending and past invitations. |
invite_member | confirm | Sends an email. Guests see nothing until a grant reaches them. |
list_access | read | Every grant, or one subject's list with inherited reach marked. |
share_access | confirm | Share one project, campaign or order — may send an invitation. |
revoke_access_grant | confirm | A guest with no grants left sees nothing at all. |
Insight Reporting, search and the link-verification read side.
get_analytics_summary | read | Workspace reporting rollups for a window you choose. |
get_analytics_insights | read | AI reading of the workspace's reporting (Pro). |
search | read | Cross-entity search: markets, domains, orders, campaigns, people. |
check_link_health | read | Are the links we bought still up. `pending` means UNKNOWN — never report it as broken. |
request_link_recheck | write | Queue an immediate re-verification of a placement. |
list_notifications | read | "What needs my attention" — the bell inbox, with an href per row. |
mark_notifications_read | write | Clear the badge, one row or all. No way to mark unread again. |
Connect a client
Claude Code
One command, project-scoped so your team gets it from the repo. The ${FLU_API_KEY} form resolves the key from the environment at startup instead of committing it:
claude mcp add --transport http influensia https://app.influensia.com/api/mcp/mcp \
--header "Authorization: Bearer ${FLU_API_KEY}" \
--scope projectOr check the equivalent .mcp.json into the project root:
{
"mcpServers": {
"influensia": {
"type": "http",
"url": "https://app.influensia.com/api/mcp/mcp",
"headers": { "Authorization": "Bearer ${FLU_API_KEY}" }
}
}
}claude.ai and Claude Desktop
Add a custom connector (Settings → Connectors → Add custom connector) with the server URL https://app.influensia.com/api/mcp/mcp. Custom connectors authenticate with OAuth — no API key needed: the server registers the client dynamically and you approve the connection in your normal Influensia web session.
Claude API — MCP connector
Building your own product on the Claude API? Attach the server straight to a Messages call — Anthropic makes the MCP connection server-side, and the model gets every tool below without any client-side plumbing:
import Anthropic from '@anthropic-ai/sdk';
const anthropic = new Anthropic();
const response = await anthropic.beta.messages.create({
model: 'claude-opus-5',
max_tokens: 16000,
betas: ['mcp-client-2025-11-20'],
mcp_servers: [{
type: 'url',
name: 'influensia',
url: 'https://app.influensia.com/api/mcp/mcp',
authorization_token: process.env.FLU_API_KEY, // flu_…
}],
tools: [{ type: 'mcp_toolset', mcp_server_name: 'influensia' }],
messages: [{
role: 'user',
content:
'Compare every offer on example-magazine.com, vet the sellers, ' +
'and draft an order proposal for the best one.',
}],
});Claude Agent SDK
For agents you host yourself (the Claude Code harness as a library), declare the server in mcpServers and allowlist its tools:
import { query } from '@anthropic-ai/claude-agent-sdk';
for await (const message of query({
prompt: 'Which of our purchased links lost their live status this week?',
options: {
mcpServers: {
influensia: {
type: 'http',
url: 'https://app.influensia.com/api/mcp/mcp',
headers: { Authorization: `Bearer ${process.env.FLU_API_KEY}` },
},
},
allowedTools: ['mcp__influensia__*'],
},
})) {
// stream progress / read the final result
}Everything else
Any MCP client that speaks Streamable HTTP works: point it at https://app.influensia.com/api/mcp/mcp and send Authorization: Bearer flu_…. Clients that implement MCP OAuth can skip the key entirely.
Agent playbooks
Worked tool sequences for the jobs people actually delegate. Each starts from whoami.
Vet and buy (buyer)
find_domainsorsearch_marketsto shortlist, thencompare_domain_offersper domain — same site, every seller's price and terms.get_marketandget_member_profileto vet: verification badges, live stats, reviews. A null completion rate is a new seller, not a bad one.request_orderwith briefs inline — and passcampaignIdwhen the user's intent names a campaign: an order's campaign is set at creation and can never be attached afterwards.- Hand off: a human funds escrow in checkout.
cancel_orderis the undo while the order is still pending.
Campaign-driven buying
list_campaigns→recommend_placements— “what should this campaign buy next”, from the campaign's own guardrails; it already excludes domains that link to the project. Prefer it overfind_domains, which knows nothing about the campaign.add_campaign_placementsto shortlist prospects (no money), or go straight torequest_orderwithcampaignId.
Link-health watch
check_link_health— every purchased placement, re-verified on a widening schedule (1, 3, 7, 30, then every 90 days).verifiedmeans the link was seen;failedmeans real evidence it is wrong or gone;pendingmeans unknown — never report it as broken.request_link_recheckfor anything the user wants re-tested now; report changes, not raw rows.
Fulfilment (seller)
list_order_items— the flat queue of incomplete work across orders.draft_order_item— start an item and write its draft in one move. Declaring a link live stays human: it triggers the buyer's verify-and-release loop.- Demand side:
list_briefswithmatchingMyInventoryshows open buyer briefs your live listings could fill. Answering one is deliberately human — an agent that mass-answered briefs would be spam.
Safety & money
The safety model is encoded in the domain, not in prompt hopes: agents propose, humans pay. request_order creates a requested order that cannot cost anything until a person funds escrow in checkout; escrow releases per link only after the platform has fetched the page and verified the agreed link.
There is deliberately no tool that:
- funds escrow, releases it, approves drafts for publication, or resolves disputes and refunds;
- accepts or sends priced offers, or answers a brief;
- subscribes, manages cards, or touches payout accounts;
- provisions credentials — API keys, 2FA, passwords, company verification, or third-party metric keys;
- writes a review — reputational statements stay human, though the reads for vetting are all there.
Where an agent produces something public under the user's name, the default is a draft: publish_article writes the words, a human decides whether they go out.