Skip to content

Deploying a Hosted Agent

So you chose the Hosted listing type — buyers subscribe and your agent runs for them automatically. This guide is for sellers who have a working agent (a script, a bot, an automation) but aren't sure how to "deploy" it or connect it to AgentDukaan. No prior DevOps experience assumed.

Prefer to let an AI write the integration? Hand the copy-paste prompts in Build & Verify Your Agent with AI to Claude/Cursor/Copilot with your code — they add (or audit) everything this guide describes. This guide is the hand-written version of the same thing.

Don't want to run a server at all? That's fine — Hosted isn't your only option. Sell your work as Source code, Done-for-you, or a Build-it guide instead (see the Seller Guide). Come back here only if you want buyers to use your agent without setting anything up themselves.


1. What "hosted" actually means

A hosted agent has two halves:

  1. Your agent — the code that does the work (sends WhatsApp messages, scrapes leads, calls an LLM, whatever). It runs on a computer that is always on and reachable from the internet over HTTPS. That computer is your "server" — you don't own hardware; you rent it from a cloud platform for a few hundred rupees a month.

  2. A webhook — a single HTTPS URL on your server that AgentDukaan calls to say "a buyer just subscribed." Your agent then pulls that buyer's credentials from AgentDukaan over a signed request, sets itself up, and starts running. Secrets (API keys, tokens) are never pushed to you — you fetch them when you need them and hold them in memory.

Buyer subscribes & fills setup wizard
        │
        ▼
AgentDukaan ──POST buyer_configured (signed)──▶ https://your-server.com/hooks/agentdukaan
        ▲                                                   │
        │  ◀──────── GET /config (pull the keys, signed) ───┤
        │                                                   ▼
        └──────────── POST /run (report usage) ──── your agent runs for that buyer

You provide that webhook URL in Step 3 of the listing creator ("Webhook endpoint"). It must be HTTPS in production.


2. The integration contract

Three messages to understand: get notified, pull the credentials, and report runs.

2a. Inbound — buyer_configured (AgentDukaan → you)

When a buyer finishes setup, AgentDukaan sends an HTTP POST to your webhook endpoint. This is a notification — it does not contain the buyer's secrets.

Headers

HeaderValue
Content-Typeapplication/json
X-AgentDukaan-Eventbuyer_configured
X-AgentDukaan-SigHMAC-SHA256 of the raw request body, hex, signed with your listing's webhook secret

Body

{
  "event": "buyer_configured",
  "subscriptionId": "665f…",     // identifies this buyer's subscription — save it
  "buyerId": "664a…",
  "listingId": "663b…",
  "tier": { "name": "Growth", "priceINR": 3999, "runLimitPerMonth": 6000, "features": [ … ] },
  "config": { "business_name": "Mehta Jewellers", "language": "hinglish" },  // NON-sensitive only
  "sensitiveFields": ["waba_token"],          // ids you must fetch (step 2b)
  "credentialsUrl": "https://api.agentdukaan.in/api/subscriptions/665f…/config",
  "ts": 1718600000000
}

You must respond with HTTP 2xx. If you don't (or you time out — the limit is 10s), AgentDukaan retries up to 3 times with exponential backoff, and the buyer's dashboard shows the setup as pending / failed instead of delivered.

On receipt: fetch the credentials (2b), cache them in memory against subscriptionId, and provision your agent. You'll get another buyer_configured whenever the buyer rotates or fixes a key — re-fetch then.

2b. Pull credentials — GET /api/subscriptions/{subscriptionId}/config (you → AgentDukaan)

The secrets (API keys, tokens — the fields you marked sensitive) are never pushed. Fetch them from the credentialsUrl, authenticated with the same HMAC as /run (the 5-minute timestamp window makes each request a short-lived token).

Headers

HeaderValue
X-AgentDukaan-Tscurrent time in milliseconds (within 5 min of server clock)
X-AgentDukaan-SigHMAC-SHA256 of `${subscriptionId}.${ts}`, hex, signed with your webhook secret

Response

{ "success": true, "data": { "config": { "business_name": "Mehta Jewellers", "waba_token": "EAAG…", "language": "hinglish" } } }

data.config is the decrypted config including the sensitive values — use it directly. Hold it in memory only; do not write it to your database or logs. Fetch on buyer_configured, cache for the process lifetime, and re-fetch on the next event. Every fetch is recorded in AgentDukaan's audit log.

For the authoritative field-by-field contract (including the reconfigure flow), see Hosted Agent — Webhook & Callback Contract.

2c. Outbound — report a run (you → AgentDukaan)

Each time your agent does a billable run for a buyer, tell AgentDukaan so run-limit enforcement and analytics work:

POST {API_URL}/api/subscriptions/{subscriptionId}/run

Headers

HeaderValue
X-AgentDukaan-Tscurrent time in milliseconds (must be within 5 minutes of the server's clock)
X-AgentDukaan-SigHMAC-SHA256 of the string `${subscriptionId}.${ts}`, hex, signed with your webhook secret

Response

{ "success": true, "data": { "allowed": true, "remaining": 5994 } }

A 429 RUN_LIMIT_EXCEEDED means the buyer hit their tier's monthly run cap — stop running until the next period.

Your webhook secret

Signing (above) uses a per-listing webhook secret that the platform provisions. Use the "Test webhook" button on your listing to confirm your endpoint is reachable and your signature check works. If you don't have a secret value to verify against yet, request it from the AgentDukaan team — it isn't currently exposed as an editable field in the listing UI.


3. Deploy your agent (step by step)

If you've never deployed anything, the easiest path is a managed platform that takes your code from GitHub and gives you a public HTTPS URL automatically. Railway and Render both have free/cheap tiers and need no server administration.

The example below is a minimal Node.js webhook receiver. The same idea works in Python, etc. — the platform doesn't care what language you use, only that you expose one HTTPS POST route.

Step 1 — Put your agent in a Git repo

Create a GitHub repository with your agent code. Add a tiny web server that exposes the webhook route.

// server.js — minimal hosted-agent webhook receiver (Node + Express)
import express from 'express'
import crypto from 'crypto'

const app = express()
// IMPORTANT: capture the RAW body so the signature matches byte-for-byte.
app.use(express.json({ verify: (req, _res, buf) => { req.rawBody = buf } }))

const SECRET  = process.env.AGENTDUKAAN_WEBHOOK_SECRET   // your per-listing secret
const API_URL = process.env.AGENTDUKAAN_API_URL || 'https://api.agentdukaan.in'

app.post('/hooks/agentdukaan', (req, res) => {
  // 1) Verify the signature
  const expected = crypto.createHmac('sha256', SECRET).update(req.rawBody).digest('hex')
  const got = req.header('X-AgentDukaan-Sig') || ''
  if (got.length !== expected.length ||
      !crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected))) {
    return res.status(401).send('bad signature')
  }

  // 2) The body has NO secrets — just ACK fast, then pull the config in the background
  const { event, subscriptionId } = req.body
  if (event === 'buyer_configured') {
    fetchConfig(subscriptionId)
      .then((config) => configCache.set(subscriptionId, config))  // in memory only — don't persist secrets
      .catch((e) => console.error('config fetch failed', subscriptionId, e))
  }

  // 3) ACK fast (do heavy work in the background)
  res.status(200).json({ ok: true })
})

app.listen(process.env.PORT || 3000, () => console.log('agent up'))
// agentdukaan.js — sign once, fetch credentials + report runs
import crypto from 'crypto'

const SECRET  = process.env.AGENTDUKAAN_WEBHOOK_SECRET
const API_URL = process.env.AGENTDUKAAN_API_URL || 'https://api.agentdukaan.in'
export const configCache = new Map()   // subscriptionId → decrypted config (memory only)

function sign(subscriptionId) {
  const ts  = String(Date.now())
  const sig = crypto.createHmac('sha256', SECRET).update(`${subscriptionId}.${ts}`).digest('hex')
  return { 'X-AgentDukaan-Ts': ts, 'X-AgentDukaan-Sig': sig }
}

// Pull the buyer's real keys when you need them. Hold in memory; re-fetch on the
// next buyer_configured event. Never write these to your DB or logs.
export async function fetchConfig(subscriptionId) {
  const res = await fetch(`${API_URL}/api/subscriptions/${subscriptionId}/config`, {
    headers: sign(subscriptionId),
  })
  if (!res.ok) throw new Error(`config fetch ${res.status}`)
  return (await res.json()).data.config
}

// Call this when your agent does a billable run.
export async function reportRun(subscriptionId) {
  const res = await fetch(`${API_URL}/api/subscriptions/${subscriptionId}/run`, {
    method: 'POST',
    headers: sign(subscriptionId),
  })
  if (res.status === 429) return { allowed: false }   // monthly limit reached
  return (await res.json()).data
}

Step 2 — Deploy it

Railway (similar on Render):

  1. Sign in at railway.app with GitHub.
  2. New Project → Deploy from GitHub repo → pick your repo.
  3. Under Variables, add AGENTDUKAAN_WEBHOOK_SECRET, AGENTDUKAAN_API_URL, and any of your own (LLM keys, DB URL, …). Never hard-code secrets in your code.
  4. Railway builds and runs it, then gives you a public URL like https://your-agent-production.up.railway.app. Make sure it serves on the port from process.env.PORT.

Step 3 — Wire it into your listing

  1. Your webhook URL is the public URL + your route, e.g. https://your-agent-production.up.railway.app/hooks/agentdukaan.
  2. Paste it into Webhook endpoint in the listing creator (Step 3).
  3. Click Test webhook — you should see a success response. Fix signature/URL issues until it's green.

Step 4 — Keep it running

  • Managed platforms restart your app on crash. Persist non-sensitive subscription state (which buyers are active, their settings) in a database so a restart doesn't lose them — but don't persist the fetched secrets; re-pull them with fetchConfig after a restart (or lazily on first use) so keys live only in memory.
  • Add basic logging so you can see incoming webhooks and run reports.
  • Watch your platform's free-tier limits; upgrade before you hit them so the agent doesn't sleep.

4. Go live

  1. Build your setup wizard so buyers can hand you the config your agent needs — see Seller Guide §5. Mark API keys/tokens sensitive.
  2. Upload a demo video.
  3. Submit for QA (requires approved KYC). Once an admin approves it, your agent is live and discoverable.

5. Troubleshooting

SymptomLikely cause
Test webhook fails / times outURL wrong, app not running, or not listening on process.env.PORT. Check your platform's logs.
401 bad signature in your logsYou hashed the parsed JSON instead of the raw body, or the secret doesn't match the platform's.
Setup shows failed/pending on the buyer's dashboardYour endpoint didn't return 2xx within 10s. ACK immediately, do heavy work after responding.
run returns 401Timestamp older than 5 min (clock drift) or signature computed over the wrong string — it must be `${subscriptionId}.${ts}`.
run returns 429Buyer hit their tier's monthly run limit; resume next period.
The webhook body has no API keysExpected — secrets are never pushed. Fetch them from credentialsUrl / GET …/config (see §2b above). The webhook only carries non-sensitive settings + sensitiveFields.
GET …/config returns 401Same causes as /run 401: clock drift >5 min, or signature computed over the wrong string (`${subscriptionId}.${ts}`).

See also