ΟδηγόςPartner Webhooks

Partner Webhooks

Πώς να λαμβάνετε events της SmartphoneKey μέσω HTTP webhooks — ρύθμιση, αυθεντικοποίηση, παράδοση και συμπεριφορά retry.

Τα partner webhooks επιτρέπουν στο σύστημά σας να λαμβάνει ειδοποιήσεις σε πραγματικό χρόνο όταν συμβαίνουν events στη SmartphoneKey. Όταν ενεργοποιείται ένα event στο οποίο έχετε εγγραφεί, η SmartphoneKey κάνει ένα HTTP POST στο καταχωρημένο endpoint σας με το payload του event.

Πώς λειτουργεί

  1. Καταχωρείτε μια συνδρομή webhook με ένα URL προορισμού και (προαιρετικά) τους τύπους event που θέλετε να λαμβάνετε.
  2. Όταν ενεργοποιείται ένα event που ταιριάζει, η SmartphoneKey το παραδίδει στο endpoint σας ως HTTP POST.
  3. Το endpoint σας απαντά με κωδικό κατάστασης 2xx για να επιβεβαιώσει τη λήψη.
  4. Αν η παράδοση αποτύχει, το AWS EventBridge κάνει αυτόματα retry.

Στο παρασκήνιο, κάθε webhook υποστηρίζεται από έναν κανόνα EventBridge + API destination που έχει παρασχεθεί (provisioned) για το partnerId σας. Το EventBridge φιλτράρει events με βάση το detail.partnerId == <your orgId>, ώστε να βλέπετε πάντα μόνο events για τον δικό σας tenant.

Διαθέσιμοι τύποι event

Η SmartphoneKey δημοσιεύει events από δύο πηγές:

ΠηγήΠεριγραφή
spk.apiDomain events από το B2C API (κλειδαριές, χρήστες, hubs, κάμερες, κλειδιά)
spk.iot.shadowShadow updates του AWS IoT από συνδεδεμένες συσκευές hub

Το detail-type κάθε event ταιριάζει με το όνομα του event σε PascalCase που παρατίθεται παρακάτω.

Lock events (spk.api)

detail-typeΠότε ενεργοποιείται
LockCreatedA new lock has been created in the system
OwnerSetA lock's owner has been assigned or changed
OrganizationSetA lock has been moved to a different organization
KeyAddedA physical key has been added to a lock
KeyRemovedA physical key has been removed from a lock
ResidentAddedA resident has been granted access to a lock
ResidentRemovedA resident's access to a lock has been revoked
LockUuidChangedA lock's UUID has been replaced
LockOpenRequestedAn unlock command was delivered to the lock
LockOpenConfirmedThe lock reported that an unlock was carried out
LockOpenUnconfirmedAn unlock was sent but the lock did not confirm the bolt moved, or did not answer in time
KeyWriteRequestedA key write to physical NFC hardware was requested
LockIvrCallCompletedAn IVR call to a resident about this lock ended (telephony-api). Delivered at least once — deduplicate on callId

Temporary key events (spk.api)

detail-typeΠότε ενεργοποιείται
TempKeyCreatedDeprecated: a temporary key has been issued through a per-lock route
TempKeyUsedDeprecated: a temporary key has been used to unlock (see TempKeyDoorOpened)
TempKeyDeletedDeprecated: a temporary key has been deleted through a per-lock route
TempKeyUpdatedDeprecated: a temporary key's rules changed through a per-lock route
TempKeyIssuedA temporary key has been issued for one or more doors
TempKeyChangedA temporary key's validity, limit, access or guest instructions changed
TempKeyRevokedA temporary key has been revoked; none of its doors open with it any more
TempKeyDoorOpenedA door has been opened with a temporary key

User events (spk.api)

detail-typeΠότε ενεργοποιείται
UserCreatedA new user has been registered
ProfileUpdatedA user's profile fields have changed
UserPhoneNumberUpdatedA user's phone number has changed (only the masked number is carried)
UserEmailUpdatedA user's email address has changed
PhysicalKeyAddedA physical key has been issued to a user
PhysicalKeyRemovedA physical key has been removed from a user
WalletKeyPreparedA wallet key (Apple/Google) is being provisioned
WalletKeyCommittedA wallet key has been issued and the pass is available
WalletKeyRolledBackA wallet key provisioning attempt was rolled back
WalletKeyRemovedA wallet key has been removed from a user
WalletKeyOrgAssignedAn admin assigned a wallet key to its issuing organization
AccessibleLockAddedA user has been granted access to a lock
AccessibleLockRemovedA user has lost access to a lock
UserSuspendedA user has been suspended
UserReactivatedA previously suspended user has been reactivated
WalletKeyAddedA wallet key was added (legacy single-step path)
LicensePlateAddedA license plate has been registered to a user
LicensePlateRemovedA license plate has been removed from a user
DeviceAddedA device has been linked to a user
DeviceRemovedA device has been unlinked from a user
DevicePushMutedUpdatedA user muted or unmuted alarm push notifications for one of their devices
CallToOpenLockChosenLocks or Matter devices were chosen to open when the user calls the call-to-open number, each on its own digit; replaces the previous choice whole; a preference, not an access grant
CallToOpenLockClearedThe user's call-to-open choice was cleared whole by a ClearCallToOpenLock command; removing a chosen lock or device from the user's lists drops that target from the choice, without this event
CallToOpenCallRecordedA call from the user's own phone number was admitted to open the user's call-to-open choice (the lock open is requested next and may still fail; LockOpenRequested on the lock is the open itself); written only for an admitted call, never for a refused one, and without the phone number; an admitted call counts towards the user's call-to-open rate limit and repeated-call check even when its open then fails
UserSubscriptionStatusUpdatedAn organization's subscription status entry for a user changed (mobile app access); carries only the changed fields (delivered only to the organization whose entry changed)

Hub events (spk.api)

detail-typeΠότε ενεργοποιείται
HubProvisionedA hub has been provisioned (manufactured / known to the system)
HubClaimedA hub has been claimed by an organization and site
HubReClaimedA hub has been moved from one organization/site to another
HubRoleSetA hub's primary/secondary role has been set
HubSuspendedA hub has been suspended
HubDecommissionedA hub has been decommissioned
MigrationStartedA hub-to-hub device migration has started
DeviceMigrationStatusUpdatedA device's migration status has changed
MigrationCompletedA hub-to-hub device migration has finished

Camera events (spk.api)

detail-typeΠότε ενεργοποιείται
CameraRegisteredA camera has been registered
CameraUpdatedA camera's metadata or stream URLs have changed
CameraDisabledA camera has been disabled
CameraDeletedA camera has been deleted
CameraLinkedToLockA camera has been associated with a lock
CameraUnlinkedFromLockA camera has been disassociated from a lock
CameraRoleCreatedA 3dEye access role has been created
CameraRoleDeletedA 3dEye access role has been deleted
CameraRoleUserAssignedA user has been added to a camera role
CameraRoleUserRemovedA user has been removed from a camera role
CameraRoleCameraAssignedA camera has been added to a role
CameraRoleCameraRemovedA camera has been removed from a role

RTSP camera events (spk.api)

detail-typeΠότε ενεργοποιείται
RtspCameraRegisteredAn RTSP camera has been registered; placement on a video worker follows
RtspCameraUpdatedAn RTSP camera's name or source has changed
RtspCameraDeletedAn RTSP camera has been deleted
RtspCameraAccessGrantedA user has been granted access to an RTSP camera
RtspCameraAccessRevokedA user's access to an RTSP camera has been revoked
RtspCameraStreamStartedAn RTSP camera's stream has been started
RtspCameraStreamStoppedAn RTSP camera's stream has been stopped
RtspCameraPlacedAn RTSP camera has been placed on a video worker and its stream confirmed
RtspCameraPlacementFailedAn RTSP camera could not be placed on any video worker
RtspCameraRecordingConfiguredAn RTSP camera's recording retention was set (0 = recording off)
RtspCameraRecordingsPurgeRequestedA purge of an RTSP camera's older recordings was requested
RtspCameraRecordingsPurgedA requested recordings purge has finished
RtspCameraRuntimeStatusChangedAn RTSP camera's runtime status changed, as reported by its video worker (delivered only for organization-owned cameras)
RtspCameraMonitoringConfiguredAn RTSP camera's availability monitoring was switched on or off
RtspCameraUnavailabilityConfirmedA monitored RTSP camera is still unavailable; repeated at most every five minutes while it stays so (delivered only for organization-owned cameras)

Video worker events (spk.api)

detail-typeΠότε ενεργοποιείται
VideoWorkerStartedA video worker (VMS box) started up; one event per organization with cameras on it, listing only that organization's cameras

Matter device events (spk.api)

detail-typeΠότε ενεργοποιείται
MatterDeviceCreatedA Matter device has been registered
ReportedShadowUpdated ¹A device reported new shadow state
DesiredShadowUpdated ¹A new desired shadow state was pushed
MatterPinSetA PIN credential slot was provisioned on a Matter door lock (the PIN itself is never carried)
MatterPinClearedA PIN credential slot was cleared on a Matter door lock
AppPinSendRequestedAn app-PIN delivery attempt was sent to the lock
AppPinShadowWriteFailedAn app-PIN write could not be made to the device shadow; the attempt will be retried
AppPinAckedThe lock acknowledged an app-PIN write
AppPinClearRequestedAn app-PIN slot clear was sent to the lock
AppPinGaveUpThe lock refused every app-PIN attempt; delivery stopped
AppPinRedrivenApp-PIN delivery was restarted from the beginning
AppPinRetiredApp-PIN delivery was dropped because the lock does not require a remote PIN
AppPinDeliveryFailedThe bridge answered an app-PIN write without the lock carrying it out; the attempt will be retried
MatterDeviceNameSetThe display name of a Matter device has been set

¹ Δεν παραδίδεται σε webhooks. Μπορεί να οριστεί σε μια συνδρομή, αλλά δεν δημοσιεύεται ποτέ στο EventBridge — εγγραφείτε στο Shadow Update για να παρακολουθείτε την κατάσταση shadow μιας συσκευής.

Alarm events (spk.api)

detail-typeΠότε ενεργοποιείται
AlarmTriggeredA Matter sensor (smoke, CO, occupancy, …) reported an alarm condition
AlarmClearedA previously triggered alarm condition has cleared
AlarmModeSetAlarm pushes of a Matter device were switched on or off

IoT events (spk.iot.shadow)

detail-typeΠότε ενεργοποιείται
Shadow UpdateAn IoT hub device updated its AWS IoT Device Shadow

Δείτε το Event Catalog για το payload κάθε event.

Payload Webhook

Το endpoint σας λαμβάνει ολόκληρο το event του EventBridge ως σώμα JSON. Το αντικείμενο detail περιέχει τα δεδομένα που είναι ειδικά για το event· για events spk.api το πραγματικό domain event βρίσκεται κάτω από το detail.data.

{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "source": "spk.api",
  "detail-type": "ResidentAdded",
  "time": "2026-04-30T10:30:00Z",
  "region": "eu-west-1",
  "account": "123456789012",
  "version": "0",
  "resources": [],
  "detail": {
    "partnerId": "acme-corp",
    "aggregateType": "lock",
    "aggregateId": "550e8400-e29b-41d4-a716-446655440000",
    "version": 7,
    "timestamp": 1745922600123,
    "data": {
      "type": "ResidentAdded",
      "lockId": 4321,
      "uuid": "550e8400-e29b-41d4-a716-446655440000",
      "userId": "9b3e1d5e-7c6a-4d4d-8b1a-2e4f6a8c0d1e",
      "name": "Jane Resident",
      "email": "resident@example.com",
      "timestamp": "2026-04-30T10:30:00.123Z"
    }
  }
}

Τα πεδία που θα χρησιμοποιείτε πιο συχνά:

ΠεδίοΣκοπός
idΤο ID του φακέλου (envelope) EventBridge που δημιουργεί η AWS — χρησιμοποιήστε το ως κλειδί deduplication
sourcespk.api ή spk.iot.shadow
detail-typeΤο όνομα του event σε PascalCase (π.χ. ResidentAdded)
detail.partnerIdΤο orgId του tenant σας — έχει ήδη φιλτραριστεί από το EventBridge
detail.aggregateType / aggregateIdΠροσδιορίζει το aggregate που παρήγαγε το event
detail.timestampΧρόνος προβολής (projection) σε epoch milliseconds (αριθμός) — διαφορετικός από το detail.data.timestamp, που είναι ο χρόνος ISO-8601 του domain event
detail.dataΤο πλήρες payload του event (δείτε το Event Catalog)

Payload IoT Shadow Update

Το source είναι spk.iot.shadow και αρκετά πεδία του detail είναι stringified JSON — ο handler σας πρέπει να καλέσει JSON.parse() σε αυτά πριν προσπελάσει τις εμφωλευμένες τιμές.

{
  "source": "spk.iot.shadow",
  "detail-type": "Shadow Update",
  "detail": {
    "partnerId": "<tenant-org-id>",
    "thingName": "<device-uuid>",
    "reportedState": "<stringified JSON of reported state>",
    "desiredState": "<stringified JSON of desired state>",
    "deltaState": "{}",
    "metadata": "<stringified IoT shadow metadata>",
    "version": 42
  }
}

Τα reportedState, desiredState, deltaState και metadata είναι stringified JSON ενσωματωμένα μέσα στο εξωτερικό αντικείμενο JSON. Κάντε parse κάθε πεδίο ξεχωριστά πριν προσπελάσετε το περιεχόμενό του.

Self-Service Εταίρων

Οι εταίροι μπορούν να διαχειρίζονται τα δικά τους διαπιστευτήρια και το webhook URL τους χωρίς να περνούν από την υποστήριξη της SmartphoneKey.

Partner Portal

Διατίθεται ένα web περιβάλλον ανά περιβάλλον (environment):

ΠεριβάλλονURL
Devhttps://partner-portal.spk-dev.workers.dev/
Stagehttps://partner-portal.spk-stage.workers.dev/
Nonprodhttps://partner-portal.spk-nonprod.workers.dev/
Prodhttps://partner-portal.spk-prod.workers.dev/

Οι εταίροι συνδέονται με Auth0 και μπορούν να:

  • Δουν το orgId τους και το ενεργό API key τους (αποκαλύπτεται μία φορά κατά την πρώτη έκδοση, μασκαρισμένο στη συνέχεια)
  • Περιστρέψουν το API key τους — ανακαλεί ακαριαία το προηγούμενο
  • Δημιουργήσουν ή ενημερώσουν το webhook URL τους

Δείτε τον οδηγό Partner Portal για μια ξενάγηση.

Ενημέρωση Webhook URL μέσω API

Αν προτιμάτε να το αυτοματοποιήσετε, μπορείτε να ενημερώσετε το webhook URL σας με το X-API-Key σας:

curl -X PATCH "https://admin-api.spk-dev.workers.dev/webhooks/$WEBHOOK_ID" \
  -H "X-API-Key: $YOUR_PARTNER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/webhooks/spk-v2"}'

Σημειώσεις:

  • Μόνο το πεδίο url είναι τροποποιήσιμο μέσω αυτού του endpoint — για να αλλάξετε τη λίστα events ή να περιστρέψετε το secret, επικοινωνήστε με τον account manager σας.
  • Το HTTPS απαιτείται σε κάθε περιβάλλον για self-service ενημερώσεις εταίρων.
  • Η κατάσταση του webhook επαναφέρεται σε pending μέχρι το EventBridge να επαληθεύσει ξανά το νέο URL.

Αυθεντικοποίηση Webhook

Ώστε το endpoint σας να μπορεί να επιβεβαιώνει ότι τα εισερχόμενα αιτήματα προέρχονται πραγματικά από τη SmartphoneKey, μια παράδοση μπορεί να φέρει ένα στατικό secret που η SmartphoneKey συμπεριλαμβάνει σε κάθε αίτημα. Αυτό ρυθμίζεται από την πλευρά του διαχειριστή στο EventBridge connection — επικοινωνήστε με τον account manager σας για να το ρυθμίσετε.

Υποστηριζόμενοι τύποι αυθεντικοποίησης

auth_typeΠεριγραφή
noneΔεν προστίθεται κεφαλίδα αυθεντικοποίησης. Αυτό είναι το προεπιλεγμένο για self-service webhooks.
apiKeyHeaderΈνα στατικό secret αποστέλλεται σε μια προσαρμοσμένη κεφαλίδα αιτήματος της επιλογής σας.
bearerTokenΈνα στατικό token αποστέλλεται στην κεφαλίδα Authorization: Bearer <token>.

Η υπογραφή σώματος HMAC ανά αίτημα δεν είναι προς το παρόν διαθέσιμη. Δεν υπάρχει HMAC X-SPK-Signature του σώματος του αιτήματος — μην χτίσετε επαλήθευση γύρω από κάτι τέτοιο. Τα webhooks που δημιουργούνται μέσω self-service έχουν προεπιλογή none· για να επισυνάψει η SmartphoneKey μια στατική κεφαλίδα ή bearer token, ζητήστε από τον account manager σας να ρυθμίσει apiKeyHeader ή bearerToken.

Επαλήθευση ενός στατικού token

Όταν έχει ρυθμιστεί μια στατική κεφαλίδα ή bearer token, συγκρίνετε την τιμή που λάβατε με το αποθηκευμένο secret σας χρησιμοποιώντας σύγκριση σταθερού χρόνου (constant-time) για να αποφύγετε επιθέσεις χρονισμού (timing attacks):

import hmac

def verify_token(received: str, expected: str) -> bool:
    return hmac.compare_digest(received.encode(), expected.encode())
import { timingSafeEqual } from 'crypto';

function verifyToken(received: string, expected: string): boolean {
  const a = Buffer.from(received);
  const b = Buffer.from(expected);
  return a.length === b.length && timingSafeEqual(a, b);
}

Ως άμυνα σε βάθος (defense in depth), επιβεβαιώστε επίσης ότι το detail.partnerId ισούται με το orgId σας, και εξυπηρετείτε πάντα το webhook endpoint σας μόνο μέσω HTTPS.

Πολιτική παράδοσης και retry

Η SmartphoneKey παραδίδει webhooks μέσω AWS EventBridge API destinations, τα οποία κάνουν retry σύμφωνα με τη ρυθμισμένη πολιτική τους:

ΠαράμετροςΤιμή
Μοντέλο παράδοσηςAt-least-once
RetriesΣύμφωνα με την πολιτική retry του EventBridge API destination (υπόκειται σε αλλαγές)
Backoff retryΕκθετικό
Triggers retryΑποκρίσεις 5xx και timeouts

Επειδή η παράδοση είναι at-least-once, σχεδιάστε το endpoint σας να είναι idempotent — χρησιμοποιήστε το πεδίο id στον φάκελο EventBridge ως κλειδί deduplication. Τα events που εξαντλούν τα retries τους απορρίπτονται· επικοινωνήστε με την υποστήριξη αν χρειάζεστε ανάκτηση events που χάθηκαν.

Απαιτήσεις απόκρισης

Το endpoint σας πρέπει να:

  • Επιστρέφει HTTP 2xx εντός του παραθύρου timeout για να επιβεβαιώσει τη λήψη.
  • Οι αποκρίσεις 4xx αντιμετωπίζονται ως μόνιμες αποτυχίες και δεν γίνονται retried.
  • Οι αποκρίσεις 5xx ή τα timeouts ενεργοποιούν retry με εκθετικό backoff.

Η επιστροφή 2xx αμέσως και η ασύγχρονη επεξεργασία συνιστάται για webhooks που ενεργοποιούν βαριά εργασία.

Φιλτράρισμα events

Κάθε συνδρομή webhook φιλτράρεται αυτόματα σε events των οποίων το detail.partnerId ισούται με το orgId σας. Δεν χρειάζεται να κάνετε φιλτράρισμα ανά event για το tenancy.

Μπορείτε επιπλέον να φιλτράρετε με βάση το detail-type (όνομα event) κατά την εγγραφή. Εγγραφείτε μόνο στους τύπους event που χρειάζεται η ενσωμάτωσή σας — αυτό μειώνει τον θόρυβο και την περιττή κίνηση προς το endpoint σας.

Βέλτιστες πρακτικές ασφάλειας

  • Επαληθεύστε το token όταν είναι ρυθμισμένο — Αν σας έχει ρυθμιστεί μια στατική κεφαλίδα ή bearer token, ελέγξτε το (constant-time) πριν την επεξεργασία, και επιβεβαιώστε ότι το detail.partnerId ταιριάζει με το orgId σας.
  • Χρησιμοποιήστε HTTPS — Καταχωρείτε μόνο HTTPS endpoints. Το HTTP απορρίπτεται εκτός του περιβάλλοντος dev.
  • Απαντήστε γρήγορα — Επιστρέψτε 2xx πριν κάνετε βαριά επεξεργασία. Βάλτε τα events σε ουρά για ασύγχρονη επεξεργασία αν χρειάζεται.
  • Να είστε idempotent — Το ίδιο event μπορεί να παραδοθεί περισσότερες από μία φορές. Χρησιμοποιήστε το id από τον φάκελο για deduplication.
  • Περιστρέφετε τα secrets περιοδικά — Ενημερώνετε το webhook secret σας τακτικά και υποστηρίξτε ένα σύντομο παράθυρο επικάλυψης κατά την περιστροφή.

Αποσφαλμάτωση παραδόσεων

Αν το endpoint σας δεν λαμβάνει events:

  1. Επιβεβαιώστε ότι το endpoint σας επιστρέφει 2xx για ένα δοκιμαστικό POST.
  2. Ελέγξτε ότι η συνδρομή σας περιλαμβάνει τους σωστούς τύπους event — περάστε events: ['*'] για να λαμβάνετε κάθε event για τον tenant σας.
  3. Επαληθεύστε ότι το URL του endpoint σας είναι προσβάσιμο από το δημόσιο internet (όχι localhost ή ιδιωτικό δίκτυο).
  4. Ελέγξτε τον έλεγχο auth/token σας (αν είναι ρυθμισμένος) — μια απόκριση 4xx από το endpoint σας αντιμετωπίζεται ως μόνιμη αποτυχία και δεν θα γίνει retried. Μόνο αποκρίσεις 5xx και timeouts ενεργοποιούν retries.
  5. Ελέγξτε αν τα events ενεργοποιούνται καθόλου χρησιμοποιώντας το Event Catalog για να επιβεβαιώσετε πότε θα έπρεπε να παράγονται τα events που περιμένετε.

Εγγραφή σε όλα τα events

Περάστε events: ['*'] κατά τη δημιουργία μιας συνδρομής webhook για να λαμβάνετε κάθε event που φέρει το partnerId σας. Το wildcard καταρρέει σε ένα κενό φίλτρο detail-type στον υποκείμενο κανόνα EventBridge — το EventBridge τότε παραδίδει τα πάντα για τον tenant σας και ο handler σας μπορεί να κάνει switch στο detail-type για να τα κατανείμει (fan out).

{
  "url": "https://your-api.example.com/webhooks/spk",
  "events": ["*"]
}

Για να περιορίσετε αργότερα, αντικαταστήστε το events με μια ρητή λίστα από detail-type σε PascalCase από τους πίνακες παραπάνω.

Σχήμα & Δημιουργία κώδικα (Code Generation)

Κατεβάστε το σχήμα OpenAPI για να σκαλώσετε (scaffold) τον webhook handler σας:

Λήψη: eventbridge-schemas.yaml

Δημιουργήστε έναν typed client:

# TypeScript
npx openapi-generator-cli generate -i eventbridge-schemas.yaml -g typescript-fetch -o ./generated

# Python
openapi-generator-cli generate -i eventbridge-schemas.yaml -g python -o ./generated

# Go
openapi-generator-cli generate -i eventbridge-schemas.yaml -g go -o ./generated

Το συνοδευτικό σχήμα προς το παρόν υστερεί σε σχέση με τον πλήρη κατάλογο events παραπάνω — η αναδημιουργία του από την πηγή b2c-api παρακολουθείται ξεχωριστά. Χρησιμοποιήστε τους πίνακες αυτής της σελίδας ως την έγκυρη (authoritative) λίστα των detail-type που εκπέμπονται στο μεταξύ.

Was this page helpful?