Meridian handbook
Destination platform (engine-dmo)

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

This feature is part of the DMO bundle (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:

templates/_shared/listing/entry.html
{% 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.

StatusMeaning
pendingSubmitted, email not yet verified
verifiedEmail verified, waiting for a decision
heldA current contact objected
approvedApproved; the claimant is an owner
rejectedRejected with a reason
withdrawnWithdrawn

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.

SignalWhenDefault weight
email_domain_matchThe claimant’s email domain is the registrable domain of the organisation’s website (method email_domain)50
listed_email_matchThe claimant’s address is the organisation’s own email or a current contact’s (method listed_email)60
contact_approvalA current contact approved the claim (method contact_approval)70
contact_objectionA current contact objected; the claim is held-100
phone_callbackStaff recorded a call back to the business (method phone_callback)60
post_code_returnedThe claimant entered the code from a posted letter (method post)60
free_provider_onlyThe address is at a free mail provider and nothing else supports the claim; withdrawn once a positive signal arrives-20
prior_rejectionThe same address had a claim on the organisation rejected in the last year-30
manualStaff attested to the claimant themselves100

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:

ActionEffect
ApproveAs above
RejectWith a reason the claimant reads
Record a call backAdds phone_callback, with the outcome and a note
AttestAdds manual, with a note
Ask the claimantMails the claimant a question
Send a letterGenerates 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.
WithdrawWithdraws the claim

Inviting someone from an organisation’s page in the admin records a claim approved by staff, with a manual signal.

Configuration

config/engine.yaml
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
KeyDefaultMeaning
claims.verification_ttl7dHow long the verification link works
claims.notify_existing_contactstrueTell every current contact when a claim is approved
claims.free_provider_listbundledThe 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.enabledtrueRecord email_domain_match
claims.methods.listed_email.enabledtrueRecord listed_email_match
claims.methods.contact_approval.enabledtrueSend current contacts the approve or object check
claims.methods.phone_callback.enabledtrueOffer staff the call back
claims.methods.post.enabledtrueOffer staff the posted letter
claims.methods.post.code_ttl30dHow long a posted code works
claims.methods.post.max_attempts5Wrong codes allowed
claims.methods.phone_auto.enabledfalseReserved: must stay false, since no automated phone verification ships
claims.signals.<signal>as in the table aboveA signal’s weight. Keys you leave out keep their defaults.
claims.auto_approve.enabledfalseLet the engine approve claims
claims.auto_approve.threshold80The 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

CommandWhat 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.