{
  "name": "UniLink",
  "description": "Link-in-bio and website builder. AI assistants connect to a person’s own UniLink account over MCP.",
  "site": "https://unil.ink",
  "verifiedAt": "2026-09-29",
  "statusLegend": {
    "live": "Works today.",
    "coming-soon": "Designed, not yet callable.",
    "not-supported": "UniLink does not offer it; see reason."
  },
  "surfaces": [
    {
      "id": "mcp",
      "name": "MCP connector",
      "status": "live",
      "entry": "https://unilink.us/mcp",
      "description": "Sign in with your UniLink account over OAuth. Works in Claude (claude.ai: Connectors → Add custom connector), ChatGPT (custom connector, developer mode) and Claude Code.",
      "docs": "https://unil.ink/agents/connect/mcp"
    },
    {
      "id": "rest",
      "name": "REST API",
      "status": "live",
      "entry": "https://unilink.us/api",
      "description": "For your own code and automations. Get a key in the Dashboard: Account settings → API Key.",
      "docs": "https://unil.ink/api-docs"
    },
    {
      "id": "skills",
      "name": "Agent skills",
      "status": "live",
      "entry": "https://unil.ink/agents/skills/unilink/SKILL.md",
      "description": "SKILL.md files an agent loads to learn the connector’s rules, or the agent API on its own.",
      "docs": "https://unil.ink/agents/connect/skills"
    },
    {
      "id": "discovery",
      "name": "Discovery",
      "status": "live",
      "entry": "https://unil.ink/llms.txt",
      "description": "llms.txt, llms-full.txt and catalog.json: plain-text and JSON maps of UniLink for models and crawlers.",
      "docs": "https://unil.ink/agents/connect/get-started"
    },
    {
      "id": "agent-profile",
      "name": "Agent profile",
      "status": "live",
      "entry": "https://unilink.us/api/agents/register",
      "description": "Building an autonomous agent? Give it its own UniLink profile, with a design, a shop and analytics.",
      "docs": "https://unil.ink/agents"
    }
  ],
  "mcp": {
    "url": "https://unilink.us/mcp",
    "transport": "streamable-http",
    "responseContentType": "application/json",
    "stateless": true,
    "sessionHeader": null,
    "protocolVersions": [
      "2025-06-18",
      "2025-03-26"
    ],
    "batching": false,
    "maxRequestBytes": 1048576,
    "auth": {
      "type": "oauth2.1",
      "grant": "authorization_code",
      "pkce": "S256",
      "accessTokenSeconds": 3600,
      "refreshTokens": "rotating, single use",
      "consent": "The person approves on a UniLink screen.",
      "revoke": "Account settings → Connected apps",
      "protectedResourceMetadata": "https://unilink.us/.well-known/oauth-protected-resource/mcp",
      "authorizationServerMetadata": "https://unilink.us/.well-known/oauth-authorization-server",
      "clientRegistration": "Client ID Metadata Documents (claude.ai, chatgpt.com); Dynamic Client Registration not supported"
    },
    "firstCall": {
      "request": "curl -i -X POST https://unilink.us/mcp",
      "response": "HTTP/1.1 401 Unauthorized\nWWW-Authenticate: Bearer resource_metadata=\"https://unilink.us/.well-known/oauth-protected-resource/mcp\", Bearer",
      "next": [
        "Read resource_metadata: it names the authorization server, https://unilink.us.",
        "Fetch https://unilink.us/.well-known/oauth-authorization-server for the authorize and token endpoints.",
        "Run the authorization code flow with PKCE (S256); the person signs in and clicks Allow.",
        "Send the access token as Authorization: Bearer on every POST."
      ],
      "note": "Only client IDs on claude.ai and chatgpt.com are accepted. Building your own client? Use the REST API with an API key."
    },
    "clients": [
      {
        "id": "claude",
        "name": "Claude (claude.ai and Claude Desktop)",
        "status": "live",
        "steps": [
          "Open Settings → Connectors → Add custom connector.",
          "Paste https://unilink.us/mcp and add it.",
          "Sign in to UniLink and click Allow."
        ]
      },
      {
        "id": "chatgpt",
        "name": "ChatGPT",
        "status": "live",
        "steps": [
          "Turn on developer mode, then add a custom connector.",
          "Use the same URL: https://unilink.us/mcp.",
          "Sign in to UniLink and click Allow."
        ]
      },
      {
        "id": "claude-code",
        "name": "Claude Code",
        "status": "live",
        "config": "claude mcp add --transport http unilink https://unilink.us/mcp",
        "steps": [
          "Run the command above.",
          "In Claude Code, run /mcp, pick unilink and sign in to UniLink.",
          "Click Allow."
        ]
      },
      {
        "id": "cursor",
        "name": "Cursor",
        "status": "not-supported",
        "reason": "Cursor needs Dynamic Client Registration, which UniLink doesn’t offer."
      }
    ],
    "errors": [
      "Every tool error comes back with isError: true and the text {\"success\":false,\"errorCode\":…,\"message\":…,\"retryable\":…} (plus \"field\" when one argument is at fault). errorCode is one of INVALID_ARGUMENT, NOT_FOUND, PAID_PLAN_REQUIRED, LIMIT_REACHED, NOT_ALLOWED, NOT_CONNECTED, INTERNAL. Only INTERNAL can be retryable. Relay the message to the person.",
      "publish_to_social and schedule_social_post skip platforms that are not connected and return NOT_CONNECTED when none of them is (connect the account in the UniLink Dashboard first; a Facebook login without a Page or a Discord connection without a channel counts as not connected). They return LIMIT_REACHED at the daily social post limit.",
      "An unknown tool or method gets a JSON-RPC error."
    ],
    "limits": [
      "add_link_item counts the profile’s live links against your plan’s link limit; at the limit it answers \"Link limit for your plan reached.\"",
      "Links to onlyfans.com, fansly.com and fanvue.com need a paid plan.",
      "Link URL: at most 511 characters. Link title: at most 512.",
      "Text block text: at most 10 000 characters, plain text. Heading: at most 512.",
      "list_repeat_customers returns at most 100 per page.",
      "publish_to_social and schedule_social_post: text at most 5000 characters, up to 10 media URLs (public https, at most 2048 characters each), and the profile’s daily social post limit (10 by default) per 24 hours.",
      "Request body: up to 1 MB."
    ],
    "docs": "https://unil.ink/agents/connect/mcp"
  },
  "tools": [
    {
      "name": "get_account_info",
      "title": "Get account and profiles",
      "description": "Get current user account info including list of profiles, plan info. Call this first to discover available profileId values.",
      "arguments": [],
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "plan": "free",
      "status": "live",
      "paginated": false
    },
    {
      "name": "get_profile_analytics",
      "title": "Get profile analytics",
      "description": "Get analytics for a profile: views, unique visitors, clicks, signups, top referrers and countries.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID (get from get_account_info)"
        },
        {
          "name": "period",
          "type": "integer",
          "required": false,
          "description": "1=Today, 2=Yesterday, 3=Week, 4=Month, 5=AllTime, 6=Custom",
          "default": 5
        },
        {
          "name": "startDate",
          "type": "string",
          "required": false,
          "description": "Start date ISO 8601 (only for period=6)"
        },
        {
          "name": "endDate",
          "type": "string",
          "required": false,
          "description": "End date ISO 8601 (only for period=6)"
        }
      ],
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "plan": "paid",
      "status": "live",
      "paginated": false
    },
    {
      "name": "list_blocks",
      "title": "List blocks",
      "description": "List all blocks on a profile. Shows type, title, visibility, position, and for link blocks each link's ID (the itemId update_link_item takes), title and URL.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        }
      ],
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "plan": "free",
      "status": "live",
      "paginated": false
    },
    {
      "name": "list_orders",
      "title": "List orders",
      "description": "List orders for a profile with pagination.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        },
        {
          "name": "take",
          "type": "integer",
          "required": false,
          "description": "Number of orders (default 20)",
          "default": 20
        },
        {
          "name": "skip",
          "type": "integer",
          "required": false,
          "description": "Offset (default 0)",
          "default": 0
        }
      ],
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "plan": "paid",
      "status": "live",
      "paginated": true
    },
    {
      "name": "list_audience",
      "title": "List audience",
      "description": "List audience members (contacts). Supports search and pagination.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        },
        {
          "name": "search",
          "type": "string",
          "required": false,
          "description": "Search by name, email, phone"
        },
        {
          "name": "take",
          "type": "integer",
          "required": false,
          "description": "Limit (default 50)",
          "default": 50
        },
        {
          "name": "skip",
          "type": "integer",
          "required": false,
          "description": "Offset (default 0)",
          "default": 0
        }
      ],
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "plan": "paid",
      "status": "live",
      "paginated": true
    },
    {
      "name": "get_audience_stats",
      "title": "Get audience stats",
      "description": "Get audience stats: contacts, forms, subscribers, customers counts.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        }
      ],
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "plan": "paid",
      "status": "live",
      "paginated": false
    },
    {
      "name": "list_products",
      "title": "List products",
      "description": "List products for a profile store with pagination.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        },
        {
          "name": "take",
          "type": "integer",
          "required": false,
          "description": "Limit (default 20)",
          "default": 20
        },
        {
          "name": "skip",
          "type": "integer",
          "required": false,
          "description": "Offset (default 0)",
          "default": 0
        }
      ],
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "plan": "paid",
      "status": "live",
      "paginated": true
    },
    {
      "name": "get_product_details",
      "title": "Get product details",
      "description": "Get detailed info about a product: price, description, media, status.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        },
        {
          "name": "productId",
          "type": "integer",
          "required": true,
          "description": "Product ID (from list_products)"
        }
      ],
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "plan": "paid",
      "status": "live",
      "paginated": false
    },
    {
      "name": "get_revenue_summary",
      "title": "Get revenue summary",
      "description": "Get revenue from paid orders, grouped by currency, optionally within a date range on the order's creation time (UTC). A paid order is one with status paid, confirmed, processing, in-progress, packed, shipped, delivered or completed. Not included: orders not yet paid, failed, cancelled, refunded or on hold. This differs from the Revenue figure on the UniLink Analytics screen, which counts only orders still in status 'paid'.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        },
        {
          "name": "startDate",
          "type": "string",
          "required": false,
          "description": "Start date ISO 8601, UTC (optional)"
        },
        {
          "name": "endDate",
          "type": "string",
          "required": false,
          "description": "End date ISO 8601, UTC (optional). A date alone (2026-01-31) includes that whole day; a full timestamp is inclusive of that exact instant."
        }
      ],
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "plan": "paid",
      "status": "live",
      "paginated": false
    },
    {
      "name": "list_repeat_customers",
      "title": "List repeat customers",
      "description": "List customers (audience contacts) who bought at least minOrders times, most purchases first, with order count, total spent per currency, and first/last purchase dates. A purchase is a paid order (status paid, confirmed, processing, in-progress, packed, shipped, delivered or completed) - the same definition as get_revenue_summary, and wider than the UniLink Analytics screen, which counts only 'paid'. Orders not linked to a contact cannot be attributed and are reported as a count only.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        },
        {
          "name": "minOrders",
          "type": "integer",
          "required": false,
          "description": "Minimum number of purchases (default 2, at least 2)",
          "default": 2
        },
        {
          "name": "take",
          "type": "integer",
          "required": false,
          "description": "Number of customers (default 20, max 100)",
          "default": 20
        },
        {
          "name": "skip",
          "type": "integer",
          "required": false,
          "description": "Offset (default 0)",
          "default": 0
        }
      ],
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "plan": "paid",
      "status": "live",
      "paginated": true
    },
    {
      "name": "create_block",
      "title": "Create a block",
      "description": "Create a new block on a profile page. Common types: link, text, faq, contact, html, video, map, timer, gallery, banner, shop, digital, appointment, event, course, donation, tip. A new link block starts with no links; add them with add_link_item. A new text block starts with placeholder text under a \"Text\" heading; replace both with update_text_block. Call list_blocks to get the new block's UID.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        },
        {
          "name": "pageId",
          "type": "integer",
          "required": false,
          "description": "Page ID (the Page shown by list_blocks). Omit or 0 for the profile's main page."
        },
        {
          "name": "type",
          "type": "string",
          "required": true,
          "description": "Block type (e.g. link, text, faq, shop, video)"
        },
        {
          "name": "position",
          "type": "integer",
          "required": false,
          "description": "Position index (0 = top)",
          "default": 0
        }
      ],
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": true
      },
      "plan": "free",
      "status": "live",
      "paginated": false
    },
    {
      "name": "update_block",
      "title": "Rename a block",
      "description": "Rename a block (its name/title). It changes nothing else: to change a block's content use add_link_item / update_link_item (link blocks) or update_text_block (text blocks).",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        },
        {
          "name": "blockUID",
          "type": "string",
          "required": true,
          "description": "Block UID (from list_blocks)"
        },
        {
          "name": "name",
          "type": "string",
          "required": true,
          "description": "New block name/title"
        }
      ],
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": true,
        "openWorldHint": true
      },
      "plan": "free",
      "status": "live",
      "paginated": false
    },
    {
      "name": "toggle_block_visibility",
      "title": "Show or hide a block",
      "description": "Show or hide a block on the profile page.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        },
        {
          "name": "blockUID",
          "type": "string",
          "required": true,
          "description": "Block UID (from list_blocks)"
        },
        {
          "name": "isActive",
          "type": "boolean",
          "required": true,
          "description": "true=visible, false=hidden"
        }
      ],
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": true
      },
      "plan": "free",
      "status": "live",
      "paginated": false
    },
    {
      "name": "reorder_blocks",
      "title": "Reorder blocks",
      "description": "Change the order of blocks on a profile page. Pass block UIDs in the desired order.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        },
        {
          "name": "blockUIDs",
          "type": "array of string",
          "required": true,
          "description": "Array of block UIDs in desired order"
        }
      ],
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": true
      },
      "plan": "free",
      "status": "live",
      "paginated": false
    },
    {
      "name": "add_link_item",
      "title": "Add a link to a link block",
      "description": "Add a link (a button to a web page) at the end of an existing LINK block. Works only on blocks of type 'link' (see list_blocks); create one first with create_block type=link if needed. The address must be an http:// or https:// web address (a bare domain such as example.com is taken as https://), at most 511 characters.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        },
        {
          "name": "blockUID",
          "type": "string",
          "required": true,
          "description": "UID of a link block (from list_blocks)"
        },
        {
          "name": "title",
          "type": "string",
          "required": true,
          "description": "Button text, at most 512 characters"
        },
        {
          "name": "url",
          "type": "string",
          "required": true,
          "description": "Web address the button opens (http or https)"
        }
      ],
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": true
      },
      "plan": "free",
      "status": "live",
      "paginated": false
    },
    {
      "name": "update_link_item",
      "title": "Edit a link in a link block",
      "description": "Change the title and/or web address of one link in a LINK block. Get itemId from list_blocks. Only links that open a web address can get a new url; for other link kinds (phone, email, page, file, group, ...) only the title can change here.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        },
        {
          "name": "blockUID",
          "type": "string",
          "required": true,
          "description": "UID of the link block (from list_blocks)"
        },
        {
          "name": "itemId",
          "type": "integer",
          "required": true,
          "description": "Link ID (from list_blocks)"
        },
        {
          "name": "title",
          "type": "string",
          "required": false,
          "description": "New button text (optional), at most 512 characters"
        },
        {
          "name": "url",
          "type": "string",
          "required": false,
          "description": "New web address, http or https (optional)"
        }
      ],
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": true,
        "openWorldHint": true
      },
      "plan": "free",
      "status": "live",
      "paginated": false
    },
    {
      "name": "update_text_block",
      "title": "Set the text of a text block",
      "description": "Set the text of an existing TEXT block (type 'text' in list_blocks), replacing what is there. A text block shows one tab unless its owner turned tabs on; this always writes the first tab. text is plain text; line breaks become paragraphs. HTML or other markup is not interpreted: it shows as typed.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        },
        {
          "name": "blockUID",
          "type": "string",
          "required": true,
          "description": "UID of a text block (from list_blocks)"
        },
        {
          "name": "text",
          "type": "string",
          "required": true,
          "description": "The new text, at most 10000 characters"
        },
        {
          "name": "title",
          "type": "string",
          "required": false,
          "description": "Heading shown above the text (optional), at most 512 characters"
        }
      ],
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": true,
        "openWorldHint": true
      },
      "plan": "free",
      "status": "live",
      "paginated": false
    },
    {
      "name": "smm_list_connected_accounts",
      "title": "List connected social accounts",
      "description": "List the social media accounts connected to a profile, per platform (facebook, instagram, twitter, linkedin, telegram, pinterest, threads, discord, youtube), marking the account publish_to_social and schedule_social_post post to. Accounts are connected in the UniLink Dashboard, not here. Call this before publishing. Not paginated.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        }
      ],
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "plan": "free",
      "status": "live",
      "paginated": false
    },
    {
      "name": "publish_to_social",
      "title": "Publish a social post",
      "description": "Publish a post now to one or more of the profile's connected social accounts: one account per platform, the one smm_list_connected_accounts marks. It creates a campaign (see list_smm_campaigns) and sends it to each platform. Platforms that are not connected are skipped; if none is connected the call fails with NOT_CONNECTED. The text lists each platform as SUCCESS, FAILED or SKIPPED; for Instagram, SUCCESS means accepted and still processing, not yet live. Each platform applies its own rules (e.g. X allows 280 characters, Instagram needs an image or video, YouTube needs a video). Paid UniLink plans only, up to the profile's daily social post limit (10 by default) per 24 hours.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        },
        {
          "name": "content",
          "type": "string",
          "required": true,
          "description": "Post text, at most 5000 characters"
        },
        {
          "name": "platforms",
          "type": "array of string",
          "required": true,
          "description": "Platforms to post to, each at most once: facebook, instagram, twitter, linkedin, telegram, pinterest, threads, discord, youtube"
        },
        {
          "name": "mediaUrls",
          "type": "array of string",
          "required": false,
          "description": "Optional public https:// image or video URLs to attach, at most 10, each at most 2048 characters. A URL whose path ends in .mp4, .mov, .avi or .webm is sent as a video, any other as an image."
        },
        {
          "name": "title",
          "type": "string",
          "required": false,
          "description": "Optional campaign title shown in list_smm_campaigns, at most 512 characters (default: the start of content)"
        }
      ],
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": true
      },
      "plan": "paid",
      "status": "live",
      "paginated": false
    },
    {
      "name": "schedule_social_post",
      "title": "Schedule a social post",
      "description": "Schedule a post to go out later to one or more of the profile's connected social accounts. Same as publish_to_social, but nothing is sent now: at scheduleAt UniLink publishes to each platform; follow it with list_smm_campaigns. Platforms that are not connected are skipped; if none is connected the call fails with NOT_CONNECTED. Paid UniLink plans only, up to the profile's daily social post limit (10 by default) per 24 hours, counted when the post is scheduled.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        },
        {
          "name": "content",
          "type": "string",
          "required": true,
          "description": "Post text, at most 5000 characters"
        },
        {
          "name": "platforms",
          "type": "array of string",
          "required": true,
          "description": "Platforms to post to, each at most once: facebook, instagram, twitter, linkedin, telegram, pinterest, threads, discord, youtube"
        },
        {
          "name": "scheduleAt",
          "type": "string",
          "required": true,
          "description": "When to publish: ISO 8601 in UTC, e.g. 2026-03-01T10:00:00Z (a time without an offset is taken as UTC). Must be in the future."
        },
        {
          "name": "mediaUrls",
          "type": "array of string",
          "required": false,
          "description": "Optional public https:// image or video URLs to attach, at most 10, each at most 2048 characters. A URL whose path ends in .mp4, .mov, .avi or .webm is sent as a video, any other as an image."
        },
        {
          "name": "title",
          "type": "string",
          "required": false,
          "description": "Optional campaign title shown in list_smm_campaigns, at most 512 characters (default: the start of content)"
        }
      ],
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": true
      },
      "plan": "paid",
      "status": "live",
      "paginated": false
    },
    {
      "name": "list_smm_campaigns",
      "title": "List social campaigns",
      "description": "List the profile's social media campaigns, newest first, with each campaign's status and platforms and each platform's post status and link. Paginated with take/skip: the text ends with a line Pagination: {\"hasMore\":true|false,\"nextSkip\":n|null}; while hasMore is true, call again with skip = nextSkip.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        },
        {
          "name": "take",
          "type": "integer",
          "required": false,
          "description": "Number of campaigns (default 20, max 100)",
          "default": 20
        },
        {
          "name": "skip",
          "type": "integer",
          "required": false,
          "description": "Offset (default 0)",
          "default": 0
        }
      ],
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "plan": "free",
      "status": "live",
      "paginated": true
    },
    {
      "name": "get_campaign_details",
      "title": "Get social campaign details",
      "description": "Get one social media campaign: status, schedule, text, and per platform the post status, link, error and engagement metrics. campaignId comes from list_smm_campaigns or from publish_to_social's result.",
      "arguments": [
        {
          "name": "profileId",
          "type": "integer",
          "required": true,
          "description": "Profile ID"
        },
        {
          "name": "campaignId",
          "type": "integer",
          "required": true,
          "description": "Campaign ID (from list_smm_campaigns)"
        }
      ],
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "plan": "free",
      "status": "live",
      "paginated": false
    }
  ],
  "workflows": [
    {
      "id": "thank-repeat-buyers",
      "title": "Thank your repeat buyers",
      "summary": "Find everyone who bought twice or more and get a short thank-you note for each.",
      "status": "live",
      "plan": "paid",
      "steps": [
        {
          "tool": "list_repeat_customers",
          "does": "Who bought at least twice, and how much."
        },
        {
          "tool": "list_audience",
          "does": "Contact details, searched by email."
        }
      ],
      "note": "Sending email is not a tool. The assistant drafts the notes; you send them.",
      "prompt": "Find everyone who bought from me at least twice on UniLink and draft a short, personal thank-you note for each. Don’t send anything."
    },
    {
      "id": "weekly-sales-summary",
      "title": "Weekly sales summary",
      "summary": "Revenue by currency, the latest orders and page traffic for the last 7 days.",
      "status": "live",
      "plan": "paid",
      "steps": [
        {
          "tool": "get_revenue_summary",
          "does": "Paid revenue per currency, last 7 days."
        },
        {
          "tool": "list_orders",
          "does": "The latest orders."
        },
        {
          "tool": "get_profile_analytics",
          "does": "Views, visitors and clicks this week."
        }
      ],
      "prompt": "Give me a UniLink sales summary for the last 7 days: revenue by currency, my latest orders, and how many people visited my page."
    },
    {
      "id": "tidy-your-page",
      "title": "Tidy your page",
      "summary": "Hide what’s stale and put the rest in a better order — after you say yes.",
      "status": "live",
      "plan": "free",
      "steps": [
        {
          "tool": "list_blocks",
          "does": "Every block, its position and visibility."
        },
        {
          "tool": "toggle_block_visibility",
          "does": "Hide the blocks you agree to hide."
        },
        {
          "tool": "reorder_blocks",
          "does": "Apply the new order."
        }
      ],
      "note": "Nothing is deleted: hidden blocks stay in your Dashboard.",
      "prompt": "Look at the blocks on my UniLink page. Suggest which to hide and a better order, and apply it only after I say yes."
    },
    {
      "id": "new-link-on-top",
      "title": "Put a new link up top",
      "summary": "Add a link to a link block, then move that block right under your header.",
      "status": "live",
      "plan": "free",
      "steps": [
        {
          "tool": "list_blocks",
          "does": "Find your link block and every block’s UID."
        },
        {
          "tool": "add_link_item",
          "does": "Add the link."
        },
        {
          "tool": "reorder_blocks",
          "does": "Move the link block first, right under the header."
        }
      ],
      "note": "Your header always stays on top. A new link goes to the end of its block; the connector can’t reorder links inside a block.",
      "prompt": "Add a link called \"New drop\" to https://example.com to my UniLink link block, then move that block right under my header. Show me the plan first."
    },
    {
      "id": "refresh-bio-text",
      "title": "Refresh your text",
      "summary": "Rewrite a text block — shorter, clearer — and save it once you approve.",
      "status": "live",
      "plan": "free",
      "steps": [
        {
          "tool": "list_blocks",
          "does": "Find the text block."
        },
        {
          "tool": "update_text_block",
          "does": "Save the new text (replaces the old)."
        }
      ],
      "note": "Works on text blocks. Your header bio isn’t editable through the connector.",
      "prompt": "Rewrite the text block on my UniLink page to be shorter and friendlier. Show me the new text and save it only after I approve."
    },
    {
      "id": "whats-working",
      "title": "See what’s working",
      "summary": "Read where visitors come from and reorder your page for them.",
      "status": "live",
      "plan": "paid",
      "steps": [
        {
          "tool": "get_profile_analytics",
          "does": "Views, clicks, top referrers and countries."
        },
        {
          "tool": "list_blocks",
          "does": "The current order."
        },
        {
          "tool": "reorder_blocks",
          "does": "Apply the order you agree on."
        }
      ],
      "note": "Analytics cover the whole page, not single blocks. The assistant suggests; you decide.",
      "prompt": "Check my UniLink analytics for this month — where visitors come from — and suggest a block order that fits them. Apply it after I agree."
    }
  ],
  "compare": [
    {
      "name": "MCP connector",
      "bestFor": "A chat assistant working for a person",
      "signIn": "OAuth — the person clicks Allow",
      "actsOn": "The person’s own account, 22 tools",
      "docs": "https://unil.ink/agents/connect/mcp"
    },
    {
      "name": "REST API",
      "bestFor": "Your own code, scripts and automations",
      "signIn": "API key (Account settings → API Key)",
      "actsOn": "Your account, through the full API",
      "docs": "https://unil.ink/api-docs"
    },
    {
      "name": "Agent profile API",
      "bestFor": "An autonomous agent that needs its own page",
      "signIn": "X-API-Key returned by POST /api/agents/register",
      "actsOn": "The agent’s own UniLink profile",
      "docs": "https://unil.ink/agents"
    }
  ],
  "skills": [
    {
      "id": "unilink",
      "name": "UniLink connector",
      "status": "live",
      "url": "https://unil.ink/agents/skills/unilink/SKILL.md",
      "description": "Teaches an agent to use the 22 connector tools well: resolve ids with list_blocks, read before write, confirm overwrites, respect plan limits."
    },
    {
      "id": "agent-platform",
      "name": "UniLink Agent Platform",
      "status": "live",
      "url": "https://unilink.us/api/agents/skill.md",
      "description": "For autonomous agents with their own profile: register, design the profile, manage blocks, sell, publish. Uses the agent API with an X-API-Key."
    }
  ],
  "links": {
    "llms": "https://unil.ink/llms.txt",
    "llmsFull": "https://unil.ink/llms-full.txt",
    "catalog": "https://unil.ink/catalog.json",
    "connectorSkill": "https://unil.ink/agents/skills/unilink/SKILL.md",
    "agentPlatformSkill": "https://unilink.us/api/agents/skill.md",
    "api": "https://unilink.us/api",
    "apiDocs": "https://unil.ink/api-docs",
    "signup": "https://unilink.us/signup?s=landing",
    "pricing": "https://unil.ink/pricing",
    "pages": {
      "hub": {
        "en": "https://unil.ink/agents/connect",
        "ua": "https://unil.ink/ua/agents/connect"
      },
      "workflows": {
        "en": "https://unil.ink/agents/connect/workflows",
        "ua": "https://unil.ink/ua/agents/connect/workflows"
      },
      "mcp": {
        "en": "https://unil.ink/agents/connect/mcp",
        "ua": "https://unil.ink/ua/agents/connect/mcp"
      },
      "skills": {
        "en": "https://unil.ink/agents/connect/skills",
        "ua": "https://unil.ink/ua/agents/connect/skills"
      },
      "getStarted": {
        "en": "https://unil.ink/agents/connect/get-started",
        "ua": "https://unil.ink/ua/agents/connect/get-started"
      },
      "toolkits": {
        "en": "https://unil.ink/agents/toolkits",
        "ua": "https://unil.ink/ua/agents/toolkits"
      }
    }
  }
}