Browse the documentation

Integrations

API reference

Integrate with our platform via the REST API: authentication, rate limits, endpoints, webhooks, and the MCP server.

Integrate with our platform using the REST API. Everything available in the interface is available through it too, and the MCP server exposes the same surface to AI agents.

Authentication

The API keys card in settings: two keys with their prefixes and the deactivate and revoke buttons
Keys are created under Settings → Integrations; the full key is shown only once, at creation.

All API requests require a Bearer token. Generate an API key from Settings > Integrations.

curl -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  https://your-domain.com/api/v1/tenders

Rate limiting

API calls are limited based on your subscription plan. Pro: 1,000/day, Enterprise: Unlimited.

Pagination

All list endpoints return paginated responses with the following structure:

{
  "data": [...],
  "total": 150,
  "page": 1,
  "page_size": 20,
  "total_pages": 8
}

Endpoints

GET /api/v1/tenders

List tenders with pagination and filtering. Required scope: tenders.read

ParameterTypeRequiredDescription
pagenumberNoPage number (default: 1)
page_sizenumberNoItems per page (max: 100)
sourcestringNoFilter by source (etimad, nupco)
agencystringNoFilter by agency name

GET /api/v1/tenders/:id

Get tender details with line items. Required scope: tenders.read

ParameterTypeRequiredDescription
idnumberYesTender ID

Search tenders by keyword (including item names). Required scope: search

ParameterTypeRequiredDescription
qstringNoSearch query
sourcestringNoFilter by source
agencystringNoFilter by agency
typestringNoFilter by tender type
date_fromstringNoStart date (ISO)
date_tostringNoEnd date (ISO)
pagenumberNoPage number (default: 1)
page_sizenumberNoItems per page (max: 100)

GET /api/v1/bids

List your organization's bids. Required scope: bids.read

ParameterTypeRequiredDescription
statusstringNoFilter by status
pagenumberNoPage number (default: 1)
page_sizenumberNoItems per page (max: 100)

GET /api/v1/favorites

List your organization's favorite tenders. Required scope: favorites.read

ParameterTypeRequiredDescription
pagenumberNoPage number (default: 1)
page_sizenumberNoItems per page (max: 100)

Webhooks

The integrations page: the API keys card and the webhooks card with one configured endpoint

Receive real-time notifications when events occur. Configure webhooks from Settings > Integrations.

Available events

  • tender.matched: New tender matches your alerts or products
  • bid.status_changed: Bid status has changed
  • deadline.approaching: Tender deadline is approaching

Signature verification

All webhook payloads are signed with HMAC-SHA256. Verify the X-Webhook-Signature header:

// Verify webhook signature
const crypto = require('crypto');
const signature = req.headers['x-webhook-signature'];
const expected = 'sha256=' + crypto
  .createHmac('sha256', webhookSecret)
  .update(JSON.stringify(req.body))
  .digest('hex');

if (signature === expected) {
  // Valid webhook
}

The MCP server

The MCP server lets AI agents use the platform through the same v1 REST API, with the same tools on two transports: a remote server the platform itself serves. What Claude connects to, and a local server over stdio for running the tools beside a checkout. There is no separate login: it authenticates with an API key. One you paste into the client yourself, or one minted for you when you approve the OAuth sign-in screen. A key is user-scoped: it carries the role of the member who created it, so it can never do more than that person, and it stops working if they leave the organization.

The remote server

The address is https://procduck.com/api/mcp (Streamable HTTP). It accepts Authorization: Bearer sk_live_…, and offers OAuth 2.1 to clients that sign in on their own — Claude's apps among them. The per-client steps are in Connect Claude.

Running the server locally (stdio)

1. Create an API key

Go to Settings > Integrations and create a key. Grant only the scopes the agent needs (for example tenders.read and search), or * for full access. Scopes and your role are independent limits and both apply: a viewer's key is read-only however it is scoped. The raw sk_live_ key is shown once at creation. Copy it then; only its SHA-256 hash is stored. API-key management tools require the * scope.

2. Point your MCP client at the server

The local server speaks stdio from a checkout of the code. Set PROCDUCK_API_KEY to the key you just created and PROCDUCK_API_URL to your deployment (it defaults to http://localhost:3000).

{
  "mcpServers": {
    "procduck": {
      "command": "npm",
      "args": ["run", "--silent", "mcp"],
      "cwd": "/path/to/procduck",
      "env": {
        "PROCDUCK_API_URL": "https://your-domain.com",
        "PROCDUCK_API_KEY": "sk_live_YOUR_API_KEY"
      }
    }
  }
}

3. Verify the key

Before wiring up the agent, confirm the key works against the REST API directly:

curl -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  https://your-domain.com/api/v1/tenders?page_size=1

What a key is allowed to do

Every request is checked against the current role of the key's creator, so demoting or removing a member immediately narrows or kills their keys. There is nothing to revoke by hand.

  • Owner: everything, including billing.
  • Admin: all reads and writes; no billing.
  • Sales: works bids, quotations, sales orders and the catalog; sale prices only. RFQ supplier responses, sourcing history, purchase orders, cost prices and the org inbox are withheld.
  • Purchasing: works bids, RFQs, sourcing and purchase orders with both sale and cost pricing; no org administration.
  • Technical: read-only product and manufacturer catalog plus tenders; pricing, quotations, RFQs, the inbox, orders and logistics are withheld.
  • Viewer: read-only. Writes are rejected regardless of scope.
  • Viewer (no pricing): read-only, and pricing endpoints and fields are withheld.

Troubleshooting

  • 401: Missing, malformed, or unknown key. Check that PROCDUCK_API_KEY is exported and starts with sk_live_.
  • 402: Your plan's daily API call limit is exhausted, or your plan does not include API access.
  • 403: The key is deactivated, its owner has left the organization, or the owner's role does not allow the action.
  • 429: Rate limit reached (100 requests per minute per key). Retry after a short pause.