Build on allus
Your company connects to people's allme profiles through services, asks for exactly the fields you need, and receives auto-updating, end-to-end-encrypted data — pulled from a change feed or pushed to a webhook, decrypted for you by an SDK in one of six languages.
Live in 5 minutes — Getting started →What allus is
allme.fyi is the consumer side: people keep one privacy-first profile (phone, email, address, documents…) and decide who sees what. allus.fyi is the business side. A company signs up in the portal, creates one or more services, and connects to its clients' allme profiles. From then on, when a connected person updates a field they shared, your records update too — no re-collection, no stale data, no copy-paste.
Every value a person shares with your service is encrypted to that service's public key on their device. The platform stores only ciphertext; your SDK decrypts it locally with the service private key. allus never sees the plaintext. (A photo or document from a field the person did not mark private is the one exception: no encrypted copy of it exists, and you receive the file itself. Data a company shares back — its own business-card details — is plaintext by design.) See the zero-knowledge model for the full picture.
The big picture
Three actors, one direction of data flow:
COMPANY (you) allus platform PERSON (allme user)
┌───────────────────┐ ┌────────────────────┐ ┌────────────────────────┐
│ Service "CRM" │ │ │ │ allme profile │
│ request fields: │ ──ask──▶│ COMPANYCODE/ │◀─connect─│ picks a field per │
│ • work_email │ │ SERVICECODE │ │ request slot, then │
│ • mobile │ │ │ │ encrypts the value │
│ • home_address │ │ stores CIPHERTEXT │◀─encrypt─│ to your service key │
└─────────┬─────────┘ │ only (zero-know.) │ └────────────────────────┘
│ └──────────┬─────────┘
│ pull: GET /api/company-data/changes (drain-on-fetch feed)
│ push: webhook POST (X-Allus-Signature)
▼ │
┌───────────────────┐ │ your SDK decrypts with the
│ SDK (6 langs) │◀───────────────────┘ service private key — your code
│ → typed values │ only ever sees plaintext conclusions
└───────────────────┘
You configure what to ask for once (in the portal or via the portal API). People connect on their own and choose which of their fields answers each request. Your code then reads a steady stream of typed, decrypted values keyed by your slugs — it never touches OAuth tokens, the field catalog, id plumbing, or the crypto.
Why build on allus
- Always-current data. A connected person edits their phone number once; every service they kept it "live" with gets the new value automatically. No re-verification campaigns.
- End-to-end encrypted. Person → service values are encrypted to your service key on the person's device. The platform is zero-knowledge — it stores ciphertext and cannot read it.
- You only see what you asked for. The response is keyed by the request slugs you defined. The person's underlying field choices are never exposed to you — or even to the API.
- Two delivery modes. Poll the change feed as often as you like (it's a cheap drain-on-fetch queue) or receive webhooks with HMAC-verified payloads.
- Six SDKs, one config. Python, TypeScript, Go, C#/.NET, Java, PHP — identical surface, all driven by one
config.json. Decryption, the token, binary fetching, and a crash-safe consumption pump are all hidden. - JSON or XML, your choice. Set
formatin config and the wire format is invisible in your code.
The shape of your integration
A complete integration is small. After you create a service and register an API client in the portal, your whole codebase is roughly this:
from allus_company_data import Client
client = Client.from_config("config.json")
def handle(change):
# one update at a time: event, person, slug, value, live, at
if change.event == "field_updated":
store(change.person_id, change.slug, change.value, live=change.live)
elif change.event in ("connection_deleted", "field_deleted"):
remove(change.person_id, change.slug)
client.process_changes(handle) # returns when the feed is empty; schedule re-runsimport { Client } from '@allus-fyi/company-data';
const client = Client.fromConfig('config.json');
await client.processChanges((change) => {
if (change.event === 'field_updated') {
store(change.personId, change.slug, change.value, change.live);
} else if (change.event === 'connection_deleted' || change.event === 'field_deleted') {
remove(change.personId, change.slug);
}
});client, err := companydata.FromConfig("config.json")
if err != nil {
log.Fatal(err)
}
err = client.ProcessChanges(func(c companydata.Change) error {
switch c.Event {
case "field_updated":
store(c.PersonID, c.Slug, c.Value, c.Live)
case "connection_deleted", "field_deleted":
remove(c.PersonID, c.Slug)
}
return nil // nil ACKS; an error RETRIES → then dead-letters
}, companydata.PumpOptions{})using Allus.CompanyData;
using var client = Client.FromConfig("config.json");
await client.ProcessChangesAsync(async change =>
{
if (change.Event == "field_updated")
Store(change.PersonId, change.Slug, change.ValueObj, change.Live);
else if (change.Event is "connection_deleted" or "field_deleted")
Remove(change.PersonId, change.Slug);
await Task.CompletedTask;
});import fyi.allme.allus.companydata.Client;
Client client = Client.fromConfig("config.json");
client.processChanges(change -> {
switch (change.event()) {
case "field_updated" ->
store(change.personId(), change.slug(), change.value(), change.live());
case "connection_deleted", "field_deleted" ->
remove(change.personId(), change.slug());
default -> { }
}
});<?php
require 'vendor/autoload.php';
use Allus\CompanyData\Client;
use Allus\CompanyData\Model\Change;
$client = Client::fromConfig('config.json');
$client->processChanges(function (Change $change): void {
if ($change->event === 'field_updated') {
store($change->personId, $change->slug, $change->value, $change->live);
} elseif (in_array($change->event, ['connection_deleted', 'field_deleted'], true)) {
remove($change->personId, $change->slug);
}
});Start here
- How it works — the mental model (services, connect handles, request vs share fields, consent, one-time vs live, the encryption model). No code. Read this first.
- Getting started — the end-to-end on-ramp: create a service, register a client, install an SDK, fetch your first updates.
- Authentication — registering a service API client,
client_credentialstokens, the deny-all whitelist model, the service private key. - Service API reference — every endpoint, JSON/XML, rate-limit tiers, the change-feed semantics, and a link to the live API explorer.
- SDKs — the six-language overview, the canonical
config.json, and per-language quickstarts. - Webhooks — the push counterpart to the change feed.
- CRM integrations — connect a CRM once and its contact records keep themselves current, with documents and signed flows as activity on the record.
- Microsoft Dynamics 365 — the setup walkthrough for Dynamics: the details the connection needs first, connecting, and what lands on a contact.
- Freshsales — setting up the Freshsales integration: the API key it uses instead of a sign-in, the extra fields you create yourself, and how contacts are matched.
- HubSpot — the setup walkthrough for the HubSpot integration: what to connect, what lands on a contact, and the limits to know about.
- monday CRM setup — the step-by-step for connecting monday: what you authorize, the board and columns the app creates, and what lands where.
- Pipedrive — setting up the Pipedrive integration: what to authorize, where each field lands, and how an existing person is matched.
- Salesforce — setting up the Salesforce integration: what to authorize and as which user, where each field lands, the one field your customers must share, and how an existing contact is matched.
- Zoho CRM — setting up the Zoho integration: what to click, what lands where on a Zoho contact, and the limits worth knowing.
Ready to build?
Create your first service and register an API client in the business portal, then follow the quickstart.