Starting state and constraints
Our September journal recorded a measurement gap: calendar-link clicks existed in analytics, while confirmed bookings needed a separate calendar review. A click could not establish that a meeting had been booked.
During implementation, our Calendly account rejected webhook subscriptions. We chose read-only API polling with a five-minute schedule. The active path had to retain cancellations, avoid turning historical imports into new conversions and keep test bookings separate from business outcomes.
Implementation and controlled verification took place on 17 October 2026. This case covers our own booking integration, not a client engagement or an increase in sales.
Architecture and delivery
1. EventBridge schedule
Invokes the reconciliation function every five minutes. The poller has no public HTTP route.
2. Lambda reads Calendly
Node.js 22 reads events and invitees with a dedicated read-only token. Pagination stays on the Calendly API origin.
3. DynamoDB records state
Stores a booking snapshot and durable event receipts. Conditional writes prevent repeat processing and a late creation event from reviving a canceled booking.
4. Matomo receives the outcome
Confirmed bookings and controlled tests use separate goals. Calendar-link clicks remain a separate intent metric.
The runtime role is scoped to the booking table and logs. The integration does not enroll invitees in marketing email or send another invitation. Analytics receives safe attribution labels and a pseudonymous identifier derived from the booking identifier, not the invitee's email.
What we verified
| Check | Observed result |
|---|---|
| Create a test appointment through Calendly | One test booking was recorded and the separate verification goal was observed. |
| Repeat polling of the same appointment | The recorded verification did not add another acknowledged conversion. |
| Cancel the test appointment | The stored booking changed to canceled; the historical creation event remained. |
| Import existing appointments | Fourteen earlier calendar records were imported without being counted as new conversions. |
| Recheck deployed state | The poller is active on Node.js 22, the five-minute schedule is enabled, and the deployed source matches the repository-rendered bundle. |
| Check the later cost-review test as well | Both controlled test booking snapshots are canceled. Neither is a customer lead. |
The 13 offline tests also pass. They exercise replay, cancellation ordering, historical imports, provider failures, attribution filtering and the prepared signed-webhook handler. The webhook handler is tested code; polling is the active production ingestion path.
Inspect the verification summary. It publishes technical checks without calendar identities, private meeting notes or credentials.
Tradeoffs and operational ownership
Polling adds delay. A booking created and canceled entirely between polls is retained as canceled and does not generate a confirmed-booking conversion. The implementation scans calendar history, which is acceptable for our small volume but needs reassessment as the calendar grows.
DynamoDB and Matomo cannot commit atomically together. An ambiguous network failure after analytics acceptance can result in another delivery attempt. Durable receipts and a delivery lease reduce duplicates; they are not an exactly-once guarantee across both systems. Booking state is the reconciliation source when counts differ.
Operators need to review failed runs and pending deliveries, reconcile cancellations and qualify each opportunity. A confirmed appointment starts as unreviewed. It does not prove attendance, a qualified lead or revenue. Historical click goals cannot be relabeled as confirmed bookings.
What transfers to another project
Agree the source of truth, event identity, retry behavior, cancellation rules and test isolation before building an integration. Verify the actual deployed package and a controlled end-to-end path as well as the unit tests.
Our CI/CD project service covers deployment and acceptance workflows. The backup and recovery guide helps frame the separate question of recovering stored state. For the front end of this journey, see the homepage rendering case.