11. Connecting the outside world

CRM City is meant to be the system of record behind your website, your shop and your other tools — not an island you copy things into. This chapter is how things get in and out.

There are five doors. You will probably use two.


11.1 Web forms

The simplest door, and the one to start with.

Build a form in the product, put it on your website, and submissions become contacts. Each submission emits form.submitted, which an automation can act on — tag them, enrol them in a welcome sequence, notify someone.

Forms carry a hidden spam trap, so you get people rather than bots without making visitors solve puzzles.


11.2 Public pages

Eight pages work without an account. Your customers, approvers and graduates use them without ever seeing a login screen, and what they see is your organisation, not ours.

Page Who opens it
Booking page Someone booking time with you
Cancel booking The same person, changing their mind
Invoice Your client, viewing what they owe
Certificate Anyone verifying a qualification
Approval An external approver making a decision
Feedback A customer rating you
Invitation Someone accepting an invite
Unsubscribe Anyone leaving your list

Each is reached through a long unguessable link and shows only what that specific person needs. There is no page that lists all your invoices, and a certificate link reveals nothing about your other students.

Everything done on these pages re-enters the system properly. A booking creates a contact and fires the same events as one you typed. This is what stops the public surface from being a side channel that leaves gaps in your record.


11.3 The REST API

For connecting your own software.

Keys are generated at Settings → API keys. Two kinds:

  • Publishable (crm_pub_…) — limited, not harmless. It reads your product and course catalogues, and it can mark an invitation as opened or accepted when it holds that invitation's link token — which also returns the invitee's name and email. It does not read your companies or your course enrolments: those name people, so they need the secret key. Anyone who sees the key can do the same.
  • Secret (crm_sec_…) — server only. Full access, including every read that names a person. Never put one in front-end code.

Each key is shown exactly once at creation, stored hashed, named, and revocable on its own. If one leaks, revoke that key; everything else keeps working. This is the same model Stripe uses and for the same reason.

Rate limits are per key: roughly 300 reads and 60 writes a minute by default. Going over returns a clear error telling you when to retry, rather than failing mysteriously.

Coverage is broad: contacts, companies, deals, groups, events, alerts, invitations, invoices (read), orders, products, bookings, courses, enrolments, certificates, webhooks, hierarchy, and a full export.

There is also a segments endpoint, which is the underrated one — ask for tag:vip, leaders, group:<id>, or inactive-30 and get the matching contacts without recreating your segmentation logic on the other side.

The important guarantee: anything done through the API fires exactly the same events as the same thing done in the interface. A deal moved by a script triggers the same automations, the same scoring and the same outgoing webhooks as a deal moved by a human. There is no second-class path.

In-product documentation is at /docs; the public version is at /developers.


11.4 Outgoing webhooks

For telling your other systems when something happens here.

Subscribe any URL to any subset of the twenty-two events. Deliveries are signed so you can verify they came from us and were not tampered with.

Failures are retried on a widening ladder — a minute, five minutes, half an hour, two hours, twelve hours — from a durable queue. Your endpoint being down for an afternoon does not lose events, and it does not slow down anything on our side.

You can see the last delivery status and error for each subscription, which is where to look first when an integration goes quiet.

One thing that will stop a delivery on purpose

If a contact has withdrawn consent for data processing (chapter 12, GDPR), events about that person do not leave for your webhook URLs. Not delayed — withheld.

This is a different question from email preferences. Preferences govern what you send to someone. This governs what happens to data about them, and a webhook sends their data to a system that is not ours. Someone who unsubscribed from your newsletter has said nothing about that; someone who withdrew data processing has.

You will not have to guess when it happens. A row appears in the subscription's delivery history with the reason consent_withdrawn, and it is never retried — this is not a failure that might succeed later, it is a delivery that must not be made.

The check runs twice: when the event happens, and again before each retry. The retry ladder reaches twelve hours, and someone can withdraw consent inside that window.

Settings → Webhooks OUT.


11.5 Incoming data (Webhooks IN)

For letting other platforms push into the CRM without writing API code.

Create a named source with a shared secret, point the other platform at the address the source gives you, and its notifications become contacts and activities. Shopify and Stripe have built-in translators; anything else can send a simple JSON shape to the generic endpoint.

Every message is verified cryptographically and journaled — accepted or rejected, with the reason. When someone says "we sent it", you can check.

Settings → Connectors has a guided setup for popular platforms; Settings → Webhooks IN is the raw version.


11.6 The MCP server (AI agents)

The newest door, and the one that will make the least sense if you have not met the protocol before.

MCP is a standard way for an AI assistant to use external tools. CRM City speaks it. An AI client — claude.ai or another — connects using one of your existing API keys and can then work with your CRM directly: look up a contact with their full context, list upcoming occasions, draft a personal message, see your automations and campaigns.

The full list is in the developer documentation at /developers.

Two safeguards are built into how it works, and they are the reason this is not alarming:

Sending is off until you switch it on. An assistant can write a personal message for a contact, and that message lands in your drafts for you to approve. It cannot overwrite a message you have already approved. The two tools that can send, a Telegram reply and an email from a department box, are off on every key until you switch them on. Once you do, an assistant using that key sends without asking you each time. Email does not send yet (see Chapter 5 — Email).

It arrives with instructions. On connection, the assistant is told how to behave with your data: a partner in relationships rather than a pickhammer, read the context before writing, propose rather than send, let the tone follow the actual relationship, and never invent facts.

If none of this means anything to you yet, you can ignore it entirely. It costs nothing when unused and changes nothing about how the product behaves for you.