Claims
How a business claims its organisation from the public site, how the claim is verified and scored from weighted signals, how staff review it, what approval does, every claims setting in engine.yaml and the claim commands.
engine-dmo
A claim is how a business that has no portal account takes control of its entry. Someone fills in a short form (“Is this your business?”), proves their email address, and the engine gathers evidence for staff: whether their address matches the business’s website or listed email, what the business’s existing contacts say, and the result of a phone call or a posted code. Staff approve or reject; on approval the claimant becomes the organisation’s owner and receives a portal invitation, and every existing contact is told, which is the control that catches a fraudulent claim after the fact.
A claim is always of an existing CRM organisation. It does not create organisations.
Linking to the claim form
The form is part of the portal, at {portal path}/claim/{organisation id} on the site’s host. Link to it from a listing page that has an organisation:
{% if entry.organisation %}
<p><a href="/members/claim/{{ entry.organisation.id }}">{{ t("listing.claim") }}</a></p>
{% endif %} A claim needs an organisation, so a listing a business could claim must have one: in a seed, an organisations/<key>.yaml with at least a name, and organisation: <key> on the listing. Use your site’s portal path in place of /members (server.portal_path, or the path in the site’s portal.yaml). There is no template helper that builds this address, so the path is written into the template. On a multisite install whose sites use different portal paths, put the link in each site’s own template root (templates/<site>/) rather than in _shared, or the shared template links one site’s visitors to another’s path.
The form is open while auth.member.registration is claim (the default). With invite_only it answers not found, while links already sent for earlier claims keep working.
How a claim proceeds
Submitted. The claimant gives their name, email address, their role at the business and an optional message. No account is needed. The form asks the browser for a small proof of work (16 bits of sha256 over its _ts value followed by a decimal number in _pow, and optional: a claim without one is only never approved automatically) and is rate limited; it answers the same way whether or not the business has contacts and whether or not the address can receive mail, so it cannot be used to find out who runs a business. The claim is pending.
Verified. The claimant follows the link mailed to them within claims.verification_ttl. To read that mail on a development install, see Reading mail in development. The engine records the automatic signals, mails each current contact of the organisation a one-click “approve” or “object” check (when contact_approval is on), notifies the staff who review claims on the site, and the claim is verified. Claims never verified expire.
Evidence gathered. Contacts answer, staff record a phone call back, or staff send a letter with a code that the claimant types into their claim’s status page. Each adds a weighted signal and the score is recomputed. An objection from a contact moves the claim to held.
Decided. Staff approve or reject from the queue. With auto_approve on, a verified claim whose score reaches the threshold and whose form passed the proof of work is approved by the engine.
On approval the claimant is linked to the organisation as owner and sent a portal invitation (which expires after portal.invitations.expiry), and, with notify_existing_contacts on, every other current contact is told. On rejection the claimant is told, with the reason. A claimant can withdraw a claim.
stateDiagram-v2 [*] --> pending : form submitted pending --> verified : email link followed<br/>within verification_ttl verified --> held : a contact objects verified --> approved : staff approve, or<br/>auto_approve at the threshold held --> approved : staff approve verified --> rejected : staff reject held --> rejected : staff reject pending --> withdrawn : withdrawn verified --> withdrawn : withdrawn held --> withdrawn : withdrawn approved --> [*] rejected --> [*] withdrawn --> [*]
A claim whose link is never followed stays pending after the link expires; the claimant can start a new one. A held claim is never approved automatically.
| Status | Meaning |
|---|---|
pending | Submitted, email not yet verified |
verified | Email verified, waiting for a decision |
held | A current contact objected |
approved | Approved; the claimant is an owner |
rejected | Rejected with a reason |
withdrawn | Withdrawn |
Signals and the score
The score is the sum of the signals’ weights, kept between 0 and 100. It is evidence for a person, not a decision.
| Signal | When | Default weight |
|---|---|---|
email_domain_match | The claimant’s email domain is the registrable domain of the organisation’s website (method email_domain) | 50 |
listed_email_match | The claimant’s address is the organisation’s own email or a current contact’s (method listed_email) | 60 |
contact_approval | A current contact approved the claim (method contact_approval) | 70 |
contact_objection | A current contact objected; the claim is held | -100 |
phone_callback | Staff recorded a call back to the business (method phone_callback) | 60 |
post_code_returned | The claimant entered the code from a posted letter (method post) | 60 |
free_provider_only | The address is at a free mail provider and nothing else supports the claim; withdrawn once a positive signal arrives | -20 |
prior_rejection | The same address had a claim on the organisation rejected in the last year | -30 |
manual | Staff attested to the claimant themselves | 100 |
The admin shows a score of 80 or more as strong, 40 or more as moderate, and anything lower as weak.
Reviewing claims
Staff with crm.claim.review on the site work the Claims queue (under Members), which shows verified and held claims first. On a claim they see the score, every signal with its detail, the contacts’ answers and the letter, and can:
| Action | Effect |
|---|---|
| Approve | As above |
| Reject | With a reason the claimant reads |
| Record a call back | Adds phone_callback, with the outcome and a note |
| Attest | Adds manual, with a note |
| Ask the claimant | Mails the claimant a question |
| Send a letter | Generates a code and a printable letter to post to the business’s address. The code is shown once. The claimant has post.code_ttl and post.max_attempts to enter it. |
| Withdraw | Withdraws the claim |
Inviting someone from an organisation’s page in the admin records a claim approved by staff, with a manual signal.
Configuration
claims:
verification_ttl: 7d
notify_existing_contacts: true
free_provider_list: bundled
methods:
email_domain: { enabled: true }
listed_email: { enabled: true }
contact_approval: { enabled: true }
phone_callback: { enabled: true }
post: { enabled: true, code_ttl: 30d, max_attempts: 5 }
signals:
email_domain_match: 50
free_provider_only: -20
auto_approve:
enabled: true
threshold: 90 | Key | Default | Meaning |
|---|---|---|
claims.verification_ttl | 7d | How long the verification link works |
claims.notify_existing_contacts | true | Tell every current contact when a claim is approved |
claims.free_provider_list | bundled | The free mail providers for free_provider_only: bundled is the list shipped with the engine; anything else is a text file, relative to the project directory, of one domain per line with # comments |
claims.methods.email_domain.enabled | true | Record email_domain_match |
claims.methods.listed_email.enabled | true | Record listed_email_match |
claims.methods.contact_approval.enabled | true | Send current contacts the approve or object check |
claims.methods.phone_callback.enabled | true | Offer staff the call back |
claims.methods.post.enabled | true | Offer staff the posted letter |
claims.methods.post.code_ttl | 30d | How long a posted code works |
claims.methods.post.max_attempts | 5 | Wrong codes allowed |
claims.methods.phone_auto.enabled | false | Reserved: must stay false, since no automated phone verification ships |
claims.signals.<signal> | as in the table above | A signal’s weight. Keys you leave out keep their defaults. |
claims.auto_approve.enabled | false | Let the engine approve claims |
claims.auto_approve.threshold | 80 | The score that approves |
check warns when auto_approve is on and the threshold is not above the largest automated signal weight (every signal but manual and contact_approval), since one signal alone could then approve a claim.
Commands
| Command | What it does |
|---|---|
engine-dmo crm claims list [--site <site>] [--status verified|held|pending|decided] | Claims on a site; verified and held by default |
engine-dmo crm claims show <id> | One claim: the score, every signal, the contacts’ answers and the letter |
engine-dmo crm claims rescore [--dry-run] | Recompute open claims’ scores under the current weights. It never approves. |
All three need the database. Run rescore after changing claims.signals.
Templates
The claim flow’s screens are portal templates: claim.html (the form), claim_sent.html, claim_status.html (where the claimant follows the claim and enters a posted code), claim_checked.html (a contact’s answer) and claim_invalid.html. engine-dmo portal scaffold --site <site> copies them for restyling with the rest of the portal; see Restyling the portal.
Portal screens and actions
How to give one site its own portal path, routes and hidden screens, declare portal screens with scoped data and portal actions that write documents or custom fields, restyle the reference portal's templates, and build a portal of your own on the member API.
Promotions
How to declare promotion slots on a site (across the site or per landing page), how members request them and staff approve them, how promoted() and promotionLabel() render paid placements with the label the law requires, and every slot setting, status and check.