Download the PHP package allus/company-data without Composer

On this page you can find all versions of the php package allus/company-data. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package company-data

allus/company-data (PHP)

The PHP SDK for the allus company-data API. Point it at a JSON config file and it hands back typed, plaintext, your-slug-keyed conclusions: for each connected person, a map of your request-field slug → plaintext value (plus whether the value is live and when it last changed).

The SDK hides everything else — the OAuth token, the field catalog, the id plumbing, the hybrid decryption, binary fetching, the changes-queue mechanics, JSON-vs-XML. The platform is zero-knowledge: the API only ever holds ciphertext, so all decryption happens inside the SDK with your service private key. The person's own field choices are never exposed — you only ever see the request slots you configured.

This SDK is one of six language ports that share an identical API surface. This manual is the PHP view of it.

Contents: TL;DR — fetch new updates · Every call · The typed value model · Webhooks · Errors · How it's wired

Deeper reference pages live in docs/: pump · errors.


TL;DR — fetch new updates

Point a config.json at your service keys:

Drain everything new, handled one update at a time:

processChanges pulls every pending change, decrypts it, and hands them to your callback ONE BY ONE, acking each only after your code returns. Crash mid-batch? The next run replays exactly what wasn't acked — nothing is lost, and the API keeps no backlog of its own. Run it on a schedule (cron / systemd timer); there is no daemon/follow mode by design. Connections, binary values, and webhooks are documented below.


Quickstart

Requires PHP ≥ 8.1, with ext-openssl and ext-json (both standard).

The package is PSR-4 autoloaded (namespace Allus\CompanyData\src/), so require 'vendor/autoload.php' and you're done — no manual includes.

1. Write a config file

A single JSON file holds everything. Any field can be overridden by an ALLUS_* env var, so secrets needn't live in the file. No SDK method ever takes a key, passphrase, or secret as an argument — they all come from here.

allus.json:

Field Required Meaning
api_url yes API base, e.g. https://api.allme.fyi.
client_id / client_secret yes The registered client_credentials credentials for one service.
service_private_key yes Path to the OpenSSL-encrypted PKCS#8 PEM you downloaded from the portal.
key_passphrase yes Decrypts that PEM in memory at startup.
account_private_key / account_passphrase only for encrypt_payload webhooks The company account key, used to unwrap an encrypted webhook envelope.
webhooks / webhook_secret webhook auth — HMAC (default) Per-webhook HMAC secrets keyed by webhook id (matched via the X-Allus-Webhook-Id header). A single-webhook service can use a flat "webhook_secret": "…" instead of the map.
webhook_bearer_token webhook auth — bearer Verify Authorization: Bearer <token> deliveries.
webhook_basic webhook auth — basic {"username","password"} — verify HTTP Basic deliveries.
webhook_header webhook auth — header {"name","value"} — verify a custom-header delivery.
webhook_auth_none webhook auth — none true — explicit opt-out; verifyWebhook always passes (use only behind your own gateway). Configure at most one webhook auth method (two+ → ConfigError).
cache_dir no (default ./allus-cache) Durable local buffer for the changes pump. Must be writable + durable.
format no (default json) Wire format json or xml. Invisible in the output.

Env overrides use the ALLUS_ prefix of the field name, e.g. ALLUS_CLIENT_SECRET, ALLUS_KEY_PASSPHRASE, ALLUS_ACCOUNT_PASSPHRASE, ALLUS_WEBHOOK_SECRET. A missing/invalid config (or an unreadable PEM / wrong passphrase) throws ConfigError at construction — fail fast.

2. First call — list a connection's values

Or fetch one connection by id:

$client = Client::fromEnv(); builds the same client entirely from ALLUS_* env vars (no file).


Every call

Client is the only object you construct. Build it from config, then:

The optional args are advanced: $http (an injected HttpClient), $logger (a Allus\CompanyData\Pump\Logger), $sleep (a callable(float): void, for tests).

requestFields()

Your request-field definitions — fetched once from GET /api/company-data/request-fields and cached for the life of the client (it types every value). Returns your request config, never the person's fields.

connections(limit, offset)

A lazy generator that auto-pages GET /api/company-data/connections?limit&offset and yields one typed Connection at a time (bounded memory for a large book). Each $conn->values[$slug] is already decrypted (or a lazy binary handle).

Heavily rate-limited. Use for the initial full sync + occasional reconciliation only — never as a poll substitute for the changes feed. The generator paces itself within the limit (backs off on Retry-After).

connection(id)

Fetch one connection by its connection id (GET /api/company-data/connections/{id}).

logs(limit, offset)

The service's activity log (GET /api/company-data/logs?limit&offset) — ops events only (email / purge / webhook), never person field data.

processChanges(handler, ...$options)

The crash-safe changes pump: drains the feed through $handler one Change at a time, durably buffering each batch before delivery, with per-item ack and retry → dead-letter → continue. Runs until the feed is empty, then returns — there is no follow/daemon mode (you schedule re-runs yourself). Delivery is at-least-once, so your handler must be idempotent (dedup on Change->id). See The changes pump for the full model.

$logger is not a processChanges option in this SDK — pass it once to the Client constructor (Client::fromConfig('allus.json', logger: $myLogger)).

Advanced changes primitives

Webhook helpers (on the client)

The webhook receiver helpers are also exposed as Client methods (they delegate to Allus\CompanyData\Webhooks\Webhooks, fully config-driven — no key/secret arguments):

The same three are available as static functions on Allus\CompanyData\Webhooks\Webhooks, which take the Config and the decrypt/type closures explicitly — but inside an app you'll almost always use the client methods. See Webhooks.


Company documents

The service can also publish documents — contracts, statements, terms, or any structured/binary payload — either broadcast to everyone connected or addressed to one person. The encryption rule is simple and automatic:

payload_kind is either 'json' (a structured value) or 'file' (raw bytes, optionally with a MIME type; for 'file' the metadata row is created first, then the bytes are uploaded — encrypted for a per-person target, raw for a broadcast).

createDocument(array $opts)

listDocuments(...) / document($id)

updateDocumentStatus / updateDocumentMetadata / deleteDocument

Reacting to a status change in the pump

When a document's lifecycle status changes, the feed/webhook emits a document_status_changed Change carrying documentId + the new status (and the usual personId / shareCode / at). Handle it alongside your field events:


The typed value model

You work with these objects and nothing else (use Allus\CompanyData\Model\…):

All model properties are public readonly.

Keyed by your slug

$conn->values['work_email']->value"[email protected]". The key is the stable, explicit slug you set per request field in the portal — rename the label freely, the slug is the contract. The person's source field is never exposed: no source slug, no field_id, not even via ->raw.

Value

Property Meaning
value The typed plaintext (see the table below).
live true if the person chose "keep connected" (auto-updates); false for a one-time snapshot.
updatedAt ?DateTimeImmutable of when this answer last changed (per-answer, rides on the Value).

Value types (from the field's type)

Field type PHP value
email, phone, url, text string
address, bank, creditcard array — the decrypted plaintext is a JSON object, parsed for you
date, date_of_birth DateTimeImmutable (date-only, UTC midnight; falls back to the raw string if it can't be parsed)
photo, document, legal_document a lazy BinaryHandle — see below
unanswered / no value null

Binary fields — the lazy BinaryHandle

A photo/document value is a BinaryHandle. Nothing is fetched or decrypted until you call ->bytes() or ->save():

->bytes() GETs the slot-keyed file endpoint, unwraps the API's {"encrypted": true, "value": <wrapper>} envelope, decrypts with your service key, parses the inner JSON envelope ({"full": "data:…"} for photos, {"file": "data:…"} for documents) and base64-decodes the data URI into the file bytes. The result is cached on the handle, so repeated calls don't re-fetch. ->save() writes crash-safely (temp file → fsync → atomic rename).

Change

A change-feed / webhook event.

Property Meaning
id The stable server change-row id — your dedup key (captured before the server delete).
event connection_created, connection_deleted, field_updated, field_deleted, consent_accepted, consent_declined, document_status_changed.
personId The person the change is about (may be null).
shareCode The person's profile share code — present on every event (may be null).
slug, value, live Present only on field_updated; value is typed exactly like Value->value (incl. a lazy BinaryHandle for binaries). Connection/consent events carry no slot/value.
documentId, status Present only on document_status_changed — the document's id and its new lifecycle status.
at ?DateTimeImmutable of the change. (There is no separate updatedAt on a change.)

->raw

Every model carries ->raw — the underlying hardened API array — for debugging or an edge case the SDK didn't model. It still never contains the person's source field.

See docs/model.md for the full reference.


The changes pump

The changes feed is a server-side drain-on-fetch queue: GET /api/company-data/changes?limit=N returns up to N events (default 100, max 500) and deletes exactly those rows in the same transaction — no offset/cursor, and the API keeps no copy afterward. So consumption can't be a plain list: a consumer crash mid-batch would lose events the API already deleted, and a huge backlog must not materialize in memory. processChanges solves both.

Per run, repeating until the feed is empty then returning:

  1. Replay first. Deliver any un-acked events already in the local buffer (from a previous crashed run), oldest-first.
  2. Drain. When the buffer is empty, fetch one batch and persist it to the durable file buffer (fsync) BEFORE handing anything out. This is the backup the API no longer has.
  3. Deliver one-by-one. For each buffered event, oldest-first: decrypt its value at delivery (never on disk), build the typed Change, call $handler.
  4. Ack / retry / dead-letter. On success, remove the event from the buffer (ack). On a handler error, retry with backoff up to maxRetries; then either move it to the dead-letter store and continue (onError='deadletter', default — one poison event never wedges the stream) or stop and re-throw (onError='halt'). A DecryptError on a buffered event (corrupt/truncated ciphertext, rotated key) is dead-lettered immediately — re-decrypting can't fix it, so it does not burn retries (under onError='halt' it re-throws). Either way it never propagates out and wedges replay.
  5. Repeat until a drain returns empty and the buffer is drained → return.

The durable buffer

Crash safety, at-least-once, and idempotency

A batch is durably buffered before any delivery, and acked per-item only after the handler succeeds. The ack can't be atomic with your side-effects — a crash between your handler's success and its ack re-delivers that event on the next run. That makes delivery at-least-once, so:

Your handler must be idempotent. Dedup on Change->id.

Change->id is the stable server change-row id, captured before the server delete, so it survives crash + replay unchanged.

No follow mode

processChanges returns when the feed empties. You schedule re-runs — a cron job, a while (true) { $client->processChanges($handler); sleep(5); } loop, a worker queue, whatever fits. The feed is cheap to poll (see Rate limits).

Worked example

If a handler keeps failing, the event lands in the dead-letter store instead of blocking the stream; inspect with $client->deadLetters() and re-drive with $client->retryDeadLetters($handle) after fixing the cause. See docs/pump.md.


Webhooks

Webhooks are the lower-latency push alternative to polling the changes feed. The platform POSTs each change event to your configured webhook URL with:

All secrets/keys come from config; the helpers take no key or secret arguments. Use the raw request body bytes (do not re-serialize a parsed body — the HMAC is over the exact bytes the platform sent, and the SDK parses XML in an XXE-safe way over those raw bytes).

In a web route — framework-agnostic (raw PHP)

If you only have $_SERVER (no getallheaders()), reconstruct the headers the SDK needs — it only reads X-Allus-Webhook-Id and X-Allus-Signature (lookup is case-insensitive):

In a PSR-7 route (e.g. Slim)

verifyWebhook / parseWebhook let you split the steps if you prefer:

Config-driven secrets

Per-webhook HMAC secrets live in the config webhooks map, keyed by webhook id; the SDK reads X-Allus-Webhook-Id off the request and looks up the matching secret. A single-webhook service can use the flat "webhook_secret": "…" shortcut (or ALLUS_WEBHOOK_SECRET). An unknown/unconfigured id ⇒ verification returns false (and handleWebhook throws WebhookError).

The encrypt_payload account-key envelope

If a webhook has encrypt_payload enabled, the body is replaced by a {"_enc":1,…} envelope encrypted to your company account key (and the HMAC is over that envelope — the final bytes sent). parseWebhook/handleWebhook unwrap it transparently using the configured account_private_key + account_passphrase, then decrypt the inner field value with the service key — so an encrypted-payload Change is identical to a plain one. If you receive such a webhook without an account_private_key configured, you get a WebhookError.

The account-key envelope uses OAEP-SHA1 (OpenSSL's default), distinct from the OAEP-SHA256 used for person field values — the SDK handles this difference internally; you only supply the account key in config.

See docs/webhooks.md.


Rate limits

Endpoint Limit Use it for
changes (the pump) generous Poll as often as you like — it's a cheap drain-on-fetch queue.
request-fields, logs moderate Occasional reads.
connections, connection(id), binary /file heavily limited Initial full sync + occasional reconciliation only — never as a poll substitute.

A 429 carries Retry-After. The SDK backs off and retries automatically:

If you catch a RateLimitError, its ->retryAfter is the seconds to wait (or null when the header was absent).


Errors

All under Allus\CompanyData\Errors\…. Same taxonomy + names across all six SDKs.

Error When
ConfigError Missing/invalid config, unreadable key file, or wrong passphrase — at construction (fail fast).
AuthError Token fetch/refresh failed (bad client_id/secret, revoked client); or a 401 survives the one automatic refresh-and-retry.
ApiError Any non-2xx from the API; carries ->status, ->errorKey (when present), and the message.
DecryptError A ciphertext wrapper is malformed, the key is wrong, or the GCM tag mismatches. Surfaces when a value is accessed/decrypted.
WebhookError Signature verification failed, or an envelope couldn't be unwrapped/parsed.
RateLimitError A 429 from a rate-limited endpoint. Subclass of ApiError (status fixed at 429); carries ->retryAfter (seconds, or null).

ApiError/RateLimitError are not final (the latter extends the former); ConfigError, AuthError, DecryptError, WebhookError are final.

See docs/errors.md.


How it's wired

Everything below is what the SDK hides so your code only ever sees conclusions.

Auth / token. An HttpClient owns a client_credentials-only token. On the first call (or when the cached token nears expiry) it POSTs client_id/client_secret to {api_url}/oauth2/token and caches the bearer token + its expiry; refresh is automatic. A mid-flight 401 triggers exactly one refresh-and-retry, then AuthError. The token is scoped server-side to one service, so every call is implicitly that service's data. The HTTP layer goes through a small Transport seam (CurlTransport by default; tests inject a fake).

Slug resolution. requestFields() is fetched once and cached; its slug→type map types every value (so address parses to an array, photo becomes a lazy binary handle, etc.). The connection/changes endpoints return values keyed by your request slug — the person's source field is dropped server-side and never reaches the SDK.

Decryption (zero-knowledge). The service private key is loaded once at construction from the configured encrypted PEM + passphrase into an in-memory phpseclib RSA key. A decryptValue closure over it is handed to every model factory and the pump — the key never appears in a method signature. Each value is a hybrid wrapper ({"_enc":1,"k":rsa_oaep_sha256(aesKey),"iv":…,"d":aes256gcm(…)}); the SDK RSA-OAEP-SHA256 (MGF1-SHA256) unwraps the AES key via phpseclib (PHP's openssl_private_decrypt can only do SHA-1 OAEP), then AES-256-GCM decrypts the payload via the openssl ext. The platform only ever holds ciphertext — it never sees your plaintext.

Binary fetch. A binary value is a lazy BinaryHandle over a slot-keyed value_url. On ->bytes()/->save() it GETs that file endpoint, unwraps the {"encrypted":true,"value":<wrapper>} envelope, runs the same service-key decrypt to a JSON file-envelope, and base64-decodes its data URI to the file bytes. (Slot-keyed, never source-field-keyed.)

The drain-on-fetch feed. processChanges delegates to a Pump wired to a fetchChanges closure (GET /changes?limit=, returning raw ciphertext events) and a decrypt closure (builds a typed Change). Because the fetch deletes the rows it returns, the pump persists each batch to the durable file buffer (ciphertext at rest) before delivery, acks per-item after your handler succeeds, and replays the buffer on restart — see The changes pump.

XML safety. When format: "xml", responses (and webhook bodies) are parsed with a hardened DOMDocument (XXE-safe: LIBXML_NONET, DOCTYPE rejected, no entity substitution). The webhook HMAC is always computed over the raw bytes, never the parsed tree.


Development

The test suite proves crypto parity with the other five SDKs against a shared, cross-language decryption fixture: it loads the PBES2 service PEM, decrypts a text wrapper to its known plaintext, and decrypts a binary wrapper through the envelope to the expected inner-bytes hash. It also runs an independent openssl CLI cross-check, so the crypto is proven platform-correct, not merely self-consistent.


All versions of company-data with dependencies

PHP Build Version
Package Version
Requires php Version >=8.1
ext-openssl Version *
ext-json Version *
phpseclib/phpseclib Version ^3
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package allus/company-data contains the following files

Loading the files please wait ...