ctify
Documentation
API key & onboarding

Actify API

Commerce-Intent API

Plug-in monetization layer for agent infrastructure (MCP servers, A2A agents, LangChain tools, RAG services, Personal Agents). Adds structured product recommendations to your service response when — and only when — the agent or user has commercial intent.

API version
v1
Base URL
https://actify-api.vercel.app

Quickstart

Five-minute integration

Request partner credentials and onboarding, issue one POST per user task, merge the optional extension field, run the call in parallel with your own work.

  1. 01

    API key & onboarding

    Email api-actify@outlook.com. You receive onboarding guidance plus ACTIFY_API_KEY and ACTIFY_BASE_URL (production default above).

  2. 02

    Make one call

    curl -X POST "$ACTIFY_BASE_URL/v1/match" \
      -H "Authorization: Bearer $ACTIFY_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"task_query": "which microphone is best for podcasting?"}'
  3. 03

    Attach to your response

    Run the Actify API call in parallel with your own LLM or business logic. Merge at the end. Use a finite client timeout tuned to when you send the response — ~1–2 s is a sensible default for many services (shorter caps fail-open more often on fast paths; see API reference).

    import asyncio
    import os
    import httpx
    
    ACTIFY_BASE = os.environ.get("ACTIFY_BASE_URL", "https://actify-api.vercel.app")
    ACTIFY_API_KEY = os.environ["ACTIFY_API_KEY"]
    
    async def call_actify_match(task: str) -> dict | None:
        try:
            async with httpx.AsyncClient(
                timeout=float(os.environ.get("ACTIFY_TIMEOUT_S", "1.5")),
            ) as c:
                r = await c.post(
                    f"{ACTIFY_BASE}/v1/match",
                    headers={"Authorization": f"Bearer {ACTIFY_API_KEY}"},
                    json={"task_query": task},
                )
                return r.json() or None
        except Exception:
            return None  # fail-open: never block your core answer
    
    @app.post("/answer")
    async def answer(req: Req):
        ad_task = asyncio.create_task(call_actify_match(req.query))
        answer  = await your_llm.respond(req.query)
        ad      = await ad_task
        if ad and ad.get("_commerce_layer"):
            answer["_commerce_layer"] = ad["_commerce_layer"]
        return answer

Concepts

Core principles

Intent gating

When there is no clear shopping intent, POST /v1/match returns an empty JSON object ({}) and your service responds as usual. Product recommendations are returned only after the intent gate confirms a purchase-oriented task.

Fail-open by design

Timeouts, server errors, and rate limits are handled fail-open: your core answer never depends on recommendations. Run the match call in parallel with a finite client timeout aligned to your response SLA (often ~1–2 s; see API reference).

Substance protection

Recommendations live in a dedicated top-level `_commerce_layer` field. Actify never rewrites or merges into your own response fields.

Link integrity

Each recommendation URL is a tracked redirect. Forward URLs exactly as returned—no shortening, rewriting, or paraphrasing—so clicks attribute correctly.

Troubleshooting

Common outcomes

  • Response is always {}

    Most traffic is non-commercial—that is normal. Test with an explicit purchase query and confirm your auth header. An empty {} with HTTP 200 means no shopping intent was detected.

  • Redirect returns 404

    Unknown or expired tracking IDs return 404; do not retry. Issue a fresh match call to obtain new recommendation URLs.

  • HTTP 422

    The JSON body accepts only task_query and optional context_metadata. Any other top-level field returns HTTP 422.

  • HTTP 429

    Treat as fail-open: ship your answer without `_commerce_layer`. Use exponential backoff with jitter. Contact the team if you need a higher quota.

Further reading

Next steps