API für Unternehmen
Ihr Kunde erteilt Ihrem Unternehmen Zugang, und in Ihrer App erscheint eine Öffnen-Schaltfläche. Keine Schlüssel wandern durch Chats, und jede Öffnung trägt den Namen des Mitarbeiters, der gedrückt hat.
So funktioniert es
- In Ihrer Oberfläche sagt der Kunde, dass eingefahren werden muss. Zuerst klären Sie zwei Dinge: Die Schranke öffnet auf einen Anruf von seiner Nummer, und sein Telefon bleibt online. Steht an der Schranke ein Entrixy-Controller, wird das Telefon gar nicht gebraucht.
- Sie erstellen eine Anfrage und zeigen deren QR-Code. Der Kunde scannt, installiert die App, fügt die Schranke hinzu und erteilt Zugang — Ihr Unternehmen wartet bereits in der Liste.
- Sie öffnen per API und nennen den Mitarbeiter. Der Kunde sieht die Öffnung mit diesem Namen im Journal und nimmt den Zugang jederzeit mit einem Tippen zurück.
Authentifizierung
There are two ways in, and the difference is what the client sees.
Signature bound to your domain
You publish a public key on your own domain and sign every call with the private one. We take the key from the domain itself, so the right to call and the right to be shown under that domain are the same thing: the file disappears, the domain moves or changes hands — the calls stop at once. There is no one-off "verified" tick to outlive reality.
https://<your domain>/.well-known/entrixy.json
{ "key": "<Ed25519 public key, 32 bytes, base64>" }
Das Paar entsteht auf Ihrer Seite; die private Hälfte sehen wir nie. Zwei Befehle genügen:
openssl genpkey -algorithm ed25519 -out entrixy-private.pem openssl pkey -in entrixy-private.pem -pubout -outform DER | tail -c 32 | base64
Der zweite gibt die Zeichenkette für die Datei aus. Der erste belässt den privaten Schlüssel bei Ihnen: Er signiert jeden Aufruf, und es gibt niemanden, dem man ihn schicken müsste.
Then each request carries four headers:
X-Entrixy-Org: your-domain.com X-Entrixy-Ts: 1750000000 // unix seconds, ±300 s X-Entrixy-Nonce: 0011…ff // 32 lowercase hex characters, used once X-Entrixy-Sig: <base64> // Ed25519 over the base string base = "<domain>.<ts>.<nonce>." + sha256(raw request body)
A repeated nonce is refused, and so is a body that does not match the signature. This is the mode where the client sees your domain and your logo.
A domain written in national characters goes into the header and into the base string in its punycode form — xn--… — the same form it takes in the address of the key file. The client is still shown the readable spelling.
Signing in PHP, for example. openssl leaves a PEM file with a 32-byte seed, while sodium signs with the 64-byte secret key, so the first three lines derive one from the other:
$pem = file_get_contents('entrixy-private.pem');
$der = base64_decode(preg_replace('/-----[^-]+-----|\s+/', '', $pem));
$secretKey = sodium_crypto_sign_secretkey(sodium_crypto_sign_seed_keypair(substr($der, -32)));
$body = json_encode($payload, JSON_UNESCAPED_UNICODE);
$ts = time();
$nonce = bin2hex(random_bytes(16)); // 32 lowercase hex characters
$base = "$domain.$ts.$nonce." . hash('sha256', $body);
$sig = base64_encode(sodium_crypto_sign_detached($base, $secretKey));
Send the body exactly as it was signed, byte for byte, together with the four headers.
A call is refused before any action when the authentication does not pass:
| Antwort | Meaning |
|---|---|
401 auth | unknown company or a wrong secret |
401 stale_timestamp | the time in the header differs from ours by more than 300 seconds |
401 bad_nonce | the nonce is not 32 hex characters |
401 replay | this nonce has already been used |
401 bad_signature | the signature does not match the body |
401 domain_key_unavailable | the key file could not be fetched from your domain |
401 sign_required | your domain already has a key, so a secret is no longer accepted |
403 org_blocked | the company is blocked |
A secret, if you have no domain
Register, issue a secret in the cabinet and put it in the body together with your company number. A secret is enough to create requests and to read their state and contents: a=request, a=status and a=objects. The client then sees only your name with a note that the domain is not confirmed — no logo, no domain.
Opening needs the domain signature. The client’s phone checks every opening of a company with the key from its domain, so a=open with a secret alone answers 401 sign_required. As soon as the key appears on your domain the secret stops working for every call: otherwise a leaked secret would speak in the name of a confirmed company.
POST https://entrixy.com/api/company.php?a=<action>
Content-Type: application/json
{ "org_id": 17, "secret": "…", … }
The examples below show the secret in the body for brevity. With the signature the same body goes without org_id and secret, and the four headers are added.
Anfrage erstellen
POST /api/company.php?a=request
{ "org_id": 17, "secret": "…", "ref": "order-517", "ttl_hours": 72 }
→ { "code": "rrGzNweV31rkHV6Q",
"url": "https://entrixy.com/c/rrGzNweV31rkHV6Q",
"expires_in_hours": 72 }
ref ist Ihre eigene Auftragsnummer, sie kommt im Status zurück. Legen Sie url in einen QR-Code und zeigen Sie ihn dem Kunden.
Zustand der Anfrage
POST /api/company.php?a=status
{ "org_id": 17, "secret": "…", "code": "rrGzNweV31rkHV6Q" }
→ { "state": "issued", "key_ref": 4821, "ref": "order-517",
"claimed_at": "2026-09-09 12:20:11", "issued_at": "2026-09-09 12:24:03" }
| Zustand | Meaning |
|---|---|
new | QR-Code erstellt, der Kunde ist noch nicht gekommen |
claimed | der Kunde hat die App geöffnet — er ist unterwegs |
issued | Zugang erteilt; key_ref damit öffnen Sie |
revoked | der Kunde hat den Zugang zurückgenommen |
abgelaufen | die Anfrage lief ab, bevor der Kunde kam |
Was der Schlüssel öffnet
The answer to a=status already carries the contents of the key, and the same list comes from a=objects — by key_ref or by the request code.
POST /api/company.php?a=objects
{ "org_id": 17, "secret": "…", "key_ref": 96 }
{
"key_ref": 96,
"numbers": [
{ "number_id": 83, "name": "Шлагбаум во двор", "type": "call", "openable": true },
{ "number_id": 84, "name": "Ворота на парковку", "type": "call", "openable": true }
]
}
number_id is what you pass to a=open; it belongs to the object and does not change when the client renames it. name is the name the client gave the object and agreed to show you — the client's own names are encrypted and we do not see them, so this one arrives only because the client sent it along with the access. Until an older app is updated the field comes as null: show the visit address instead. openable is false for an object that only its holder can open — a call from the company will be refused.
When a key covers a single object, number_id in a=open may be omitted.
Öffnen
POST /api/company.php?a=open
Content-Type: application/json
X-Entrixy-Org: your-domain.com
X-Entrixy-Ts: 1750000000
X-Entrixy-Nonce: 9f86d081884c7d659a2feaa0c55ad015
X-Entrixy-Sig: <base64>
{ "key_ref": 4821, "number_id": 93, "actor": "Ivan P., shift 12" }
→ { "ok": 1 }
actor ist Pflicht: genau das sieht der Kunde im Journal. Alles, was den Mitarbeiter bei Ihnen identifiziert, taugt — Name, Personalnummer, Schicht. Ohne ihn wird der Aufruf abgewiesen.
| Antwort | Meaning |
|---|---|
401 sign_required | the call is not signed with the domain key: a secret is not enough to open |
400 bad_input | key_ref is missing |
400 number_id_required | the key covers several objects and the object was not named |
400 actor_required | der Mitarbeiter wurde nicht genannt |
400 unsupported_object | this object cannot be opened by a company: its openable is false |
403 revoked | the client took the access back, the key is not yours, or the object is not in it |
403 expired | die Lebensdauer des Schlüssels ist abgelaufen |
429 rate_limit | zu viele Öffnungen mit einem Schlüssel pro Minute |
The authentication answers from the table above apply here too.
Ihre Karte in der App
Senden Sie uns bei der Registrierung Namen und ein quadratisches Logo. Der Kunde sieht sie auf der gescannten Seite, im Einwilligungsdialog und neben jeder Zeile im Journal — so ist klar, wen er hereinlässt. Das Logo liefern wir selbst aus; Bilder von fremden Servern holen wir nicht.
Wenn der Kunde den Zugang zurücknimmt
The client removes the company in their app — the key disappears at once. a=open answers revoked, a=objects the same, and a=status shows the request as revoked with an empty list of objects. Nothing else has to be done on your side: a revoked access never comes back, a new visit needs a new request.
If you saved a callback address, we also ping it on the events — claimed, issued, revoked:
POST <your callback address>
{ "event": "revoked", "org_id": 5, "code": "eAc9SZ8mmeegD_GJ", "key_ref": 96 }
The address must start with https. The ping waits 3 seconds for an answer and does not follow redirects.
The ping is a nudge, not the source of truth: it carries no object list and is sent once, without retries. Take the details from a=status — that call is signed and answers the same thing whether the ping arrived or not.
Erste Schritte
Registrieren Sie das Unternehmen in Ihrem Konto — ein Formular, und es arbeitet sofort. Domain, Secret, Rückmeldeadresse, Logo und die Liste der Anfragen liegen im Unternehmenskonto.