Polemica

Developers

Polemica CRM
API Reference.

Every endpoint in the Polemica CRM — companies, contacts, deals, quotes, payments, and the lead-gate scanner. Built to be used directly, or handed straight to an AI agent.

Setup

Two things you need

Base URL

https://your-subdomain.polemica.ca

API Key

pk_crm_…

Settings → API & Integrations

Every request below uses these two values. The master key (pk_crm_…) unlocks every endpoint. A separate scanner key (sk_scan_…) is scoped to POST /scanner/scans only.

Authentication

One header, either name

X-Api-Key: {API_KEY}

OR

Authorization: Bearer {API_KEY}

For AI Agents

Paste this into your agent

Copy the block below into ChatGPT, Claude, or any agent's custom instructions. It knows every endpoint, which fields are required, and the order operations need to happen in — like fetching pipeline stages before creating a deal.

# Polemica CRM — API Reference

## SETUP — Ask the user these two questions before anything else:
1. "What is your Polemica base URL?" (format: https://your-subdomain.polemica.ca)
2. "What is your Polemica API key?" (format: pk_crm_…, found in Settings → API & Integrations)

Store answers as BASE_URL and API_KEY. Use them in every request below.
Do NOT make any API call until you have both values confirmed by the user.

## Authentication
Header: X-Api-Key: {API_KEY}
OR:     Authorization: Bearer {API_KEY}
Master key (pk_crm_…) unlocks all endpoints.
Scanner key (sk_scan_…) is for POST /scanner/scans only.

## Global Search
GET {BASE_URL}/search?q={query}
Use this first when user mentions a name, company, or email.

## Companies
GET  {BASE_URL}/companies                   params: q, province, limit, offset
GET  {BASE_URL}/companies/{id}
POST {BASE_URL}/companies                   body: { name(req), domain, website, industry, size, city, province, phone }
GET  {BASE_URL}/companies/{id}/contacts

## Contacts
GET  {BASE_URL}/contacts                    params: q, company_id, limit
GET  {BASE_URL}/contacts/{id}
POST {BASE_URL}/contacts                    body: { name(req), email, phone, title, company_id, linkedin_url }

## Pipeline / Deals
GET  {BASE_URL}/pipelines                   ← call this first to get pipeline IDs
GET  {BASE_URL}/pipelines/{id}/stages       ← call this to get stage IDs
GET  {BASE_URL}/pipelines/{id}/cards
GET  {BASE_URL}/cards/{id}
POST {BASE_URL}/cards                       body: { stage_id(req), title(req), contact_name, contact_email, contact_phone, value, source, company_id }
POST {BASE_URL}/cards/{id}/notes            body: { text(req), author }
POST {BASE_URL}/cards/{id}/tasks            body: { title(req), due_date, assignee }

RULE: Always call GET /pipelines then GET /pipelines/{id}/stages before creating a deal.

## Products
GET  {BASE_URL}/products                    params: q, category
GET  {BASE_URL}/products/{id}
GET  {BASE_URL}/products/sku/{sku}
GET  {BASE_URL}/products/categories

## Quotes
GET  {BASE_URL}/quotes
GET  {BASE_URL}/quotes/{id}
POST {BASE_URL}/quotes                      body: { company_id, contact_name, currency(default CAD), valid_until, notes }
POST {BASE_URL}/quotes/{id}/lines           body: { description(req), qty(req), unit_price(req), product_id, discount_pct, tax_pct(default 5) }

## Payments
GET  {BASE_URL}/payments
POST {BASE_URL}/payments                    body: { amount(req), currency, method(e-transfer/credit_card/cash), quote_id, company_id, note }

## Shipments
GET  {BASE_URL}/shipments
POST {BASE_URL}/shipments                   body: { quote_id, carrier, tracking, destination, shipped_at }

## Communications
GET  {BASE_URL}/communications
GET  {BASE_URL}/communications/threads
POST {BASE_URL}/communications              body: { channel(req: email/call/sms/meeting), direction(req: inbound/outbound), body, contact_email, card_id }

## Inbox
GET  {BASE_URL}/inbox/forms
GET  {BASE_URL}/inbox/bookings
POST {BASE_URL}/inbox/forms/{id}/to-crm

## Scanner / Lead Gates
POST {BASE_URL}/scanner/scans               key: sk_scan_… — body: { url(req), scan_data(req), label }
                                            returns: { scan_id, token, result_url }
POST {BASE_URL}/scanner/leads               public, no key — body: { token(req), email(req), name, phone, company }
GET  {BASE_URL}/scanner/results/{token}     only works after lead submitted

## Errors
200 OK | 201 Created | 400 Bad request | 401 Invalid key | 403 Forbidden | 404 Not found | 422 Missing field
Error body: { "detail": "message" }

## Agent rules
- Always confirm with user before creating, updating, or deleting records.
- Search first (GET /search) before creating, to avoid duplicates.
- If a required field is missing, ask the user — do not guess.

Endpoints

Full reference

Global Search

  • GET/search?q={query}

Use this first when a name, company, or email comes up — before creating a new record.

Companies

  • GET/companiesparams: q, province, limit, offset
  • GET/companies/{id}
  • POST/companiesbody: name(req), domain, website, industry, size, city, province, phone
  • GET/companies/{id}/contacts

Contacts

  • GET/contactsparams: q, company_id, limit
  • GET/contacts/{id}
  • POST/contactsbody: name(req), email, phone, title, company_id, linkedin_url

Pipeline / Deals

  • GET/pipelinescall first to get pipeline IDs
  • GET/pipelines/{id}/stagescall to get stage IDs
  • GET/pipelines/{id}/cards
  • GET/cards/{id}
  • POST/cardsbody: stage_id(req), title(req), contact_name, contact_email, contact_phone, value, source, company_id
  • POST/cards/{id}/notesbody: text(req), author
  • POST/cards/{id}/tasksbody: title(req), due_date, assignee

Always call GET /pipelines then GET /pipelines/{id}/stages before creating a deal.

Products

  • GET/productsparams: q, category
  • GET/products/{id}
  • GET/products/sku/{sku}
  • GET/products/categories

Quotes

  • GET/quotes
  • GET/quotes/{id}
  • POST/quotesbody: company_id, contact_name, currency(default CAD), valid_until, notes
  • POST/quotes/{id}/linesbody: description(req), qty(req), unit_price(req), product_id, discount_pct, tax_pct(default 5)

Payments

  • GET/payments
  • POST/paymentsbody: amount(req), currency, method(e-transfer/credit_card/cash), quote_id, company_id, note

Shipments

  • GET/shipments
  • POST/shipmentsbody: quote_id, carrier, tracking, destination, shipped_at

Communications

  • GET/communications
  • GET/communications/threads
  • POST/communicationsbody: channel(req: email/call/sms/meeting), direction(req: inbound/outbound), body, contact_email, card_id

Inbox

  • GET/inbox/forms
  • GET/inbox/bookings
  • POST/inbox/forms/{id}/to-crm

Scanner / Lead Gates

  • POST/scanner/scanskey: sk_scan_… — body: url(req), scan_data(req), label → returns scan_id, token, result_url
  • POST/scanner/leadspublic, no key — body: token(req), email(req), name, phone, company
  • GET/scanner/results/{token}only works after a lead is submitted

Errors

Status codes

200 OK201 Created400 Bad request401 Invalid key403 Forbidden404 Not found422 Missing field

Error responses have the shape { "detail": "message" }.

Agent Rules

Built-in guardrails

  • Always confirm with the user before creating, updating, or deleting records.
  • Search first (GET /search) before creating, to avoid duplicates.
  • If a required field is missing, ask the user — do not guess.

FAQ

Common questions

Where do I get my API key?

In your Polemica CRM, go to Settings → API & Integrations. The master key starts with pk_crm_ and unlocks every endpoint below. A separate scanner key (sk_scan_…) is issued for the lead-gate scanner endpoint only.

Can I give this page to an AI agent (ChatGPT, Claude, etc.)?

Yes — that's what the copy block below is for. Paste it into your agent's custom instructions along with your base URL and API key, and it knows every endpoint, required field, and the order operations need to happen in (e.g. fetching pipeline stages before creating a deal).

Is the scanner lead endpoint really public?

Yes. POST /scanner/leads is intentionally unauthenticated — it is what your public-facing lead-gate scanner calls when a visitor submits their email to unlock a scan result. Every other endpoint requires a key.

What happens if I send a request with a missing required field?

You get a 422 with a JSON body like { "detail": "message" } naming the missing field. Required fields are marked (req) in each endpoint's body definition below.