<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sergey Boyko</title>
    <description>The latest articles on DEV Community by Sergey Boyko (@sergeyboyko).</description>
    <link>https://dev.to/sergeyboyko</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4237751%2F793aa6d6-4628-4ae3-ab49-9b6ca1a982a6.png</url>
      <title>DEV Community: Sergey Boyko</title>
      <link>https://dev.to/sergeyboyko</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sergeyboyko"/>
    <language>en</language>
    <item>
      <title>From Booking Requests to Confirmed Bookings: Designing Reliable Server-Side Events</title>
      <dc:creator>Sergey Boyko</dc:creator>
      <pubDate>Wed, 11 Nov 2026 05:09:12 +0000</pubDate>
      <link>https://dev.to/sergeyboyko/from-booking-requests-to-confirmed-bookings-designing-reliable-server-side-events-3i67</link>
      <guid>https://dev.to/sergeyboyko/from-booking-requests-to-confirmed-bookings-designing-reliable-server-side-events-3i67</guid>
      <description>&lt;p&gt;A form submission is easy to track. A confirmed booking is harder.&lt;/p&gt;

&lt;p&gt;In booking-based businesses, the customer may submit a request, reserve multiple seats, change plans, or cancel before attending. If all of those actions are counted as conversions, marketing reports can look successful while overstating actual business outcomes.&lt;/p&gt;

&lt;p&gt;I worked on this problem in a booking attribution project where the goal was to connect campaign information, booking requests, CRM status changes, and analytics events.&lt;/p&gt;

&lt;p&gt;This article describes the event-design decisions and testing approach. The code examples are simplified illustrations, not copies of production code.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Define the business outcome before the event
&lt;/h2&gt;

&lt;p&gt;The first challenge was semantic, not technical.&lt;/p&gt;

&lt;p&gt;Consider these stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Request created:&lt;/strong&gt; Someone submits a booking form.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Booking confirmed:&lt;/strong&gt; The booking is accepted and recorded as confirmed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Booking cancelled:&lt;/strong&gt; A previously requested or confirmed booking is cancelled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attendance recorded:&lt;/strong&gt; Someone actually shows up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These stages are related, but they are not interchangeable.&lt;/p&gt;

&lt;p&gt;A request tells us that someone expressed interest. A confirmation tells us that the business secured a booking. Attendance tells us something different again.&lt;/p&gt;

&lt;p&gt;The tracking model must preserve those distinctions.&lt;/p&gt;

&lt;p&gt;In our project, the primary marketing outcome was a &lt;strong&gt;confirmed booking&lt;/strong&gt;, not simply a form submission.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Model events around state transitions
&lt;/h2&gt;

&lt;p&gt;Rather than sending an indistinguishable conversion every time something changes, define separate event types.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;booking_created
booking_confirmed
booking_cancelled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A useful event record might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;"event_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"booking_confirmed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"booking_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"example-123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"event_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"example-123:confirmed:1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"seat_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"facebook"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"campaign"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"example_campaign"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"occurred_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2026-10-15T14:40:00Z"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an illustrative schema, not an actual customer record.&lt;/p&gt;

&lt;p&gt;Several properties are important:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;booking_id&lt;/code&gt; connects the event to a business record.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;event_id&lt;/code&gt; identifies the event for duplicate protection.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;seat_count&lt;/code&gt; separates reservations from reserved places.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;source&lt;/code&gt; and &lt;code&gt;campaign&lt;/code&gt; preserve available attribution context.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;occurred_at&lt;/code&gt; records when the business event happened.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A confirmation should be generated from the actual booking state, not inferred from a thank-you page.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Connect the website, CRM, and reporting pipeline
&lt;/h2&gt;

&lt;p&gt;The implementation connects several parts of an existing booking system.&lt;/p&gt;

&lt;p&gt;The simplified data flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Campaign / source information
          |
          v
     Booking form
          |
          v
     Booking record
          |
          v
   CRM status transition
          |
          v
   Server-side event queue
          |
          +----&amp;gt; Google Analytics 4
          |
          +----&amp;gt; Meta Conversions API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The website captures a request. The booking system remains responsible for booking status. The integration layer turns relevant state changes into analytics events.&lt;/p&gt;

&lt;p&gt;This separation matters because marketing tools should not decide whether a customer has been confirmed. That decision belongs to the business system.&lt;/p&gt;

&lt;p&gt;The server-side layer can then provide consistent delivery, validation, and retry handling.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Make event processing idempotent
&lt;/h2&gt;

&lt;p&gt;Duplicate events are one of the easiest ways to corrupt conversion reporting.&lt;/p&gt;

&lt;p&gt;They can occur when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A webhook is delivered more than once.&lt;/li&gt;
&lt;li&gt;A request times out and gets retried.&lt;/li&gt;
&lt;li&gt;A scheduled job processes a previously attempted event.&lt;/li&gt;
&lt;li&gt;A status update is replayed.&lt;/li&gt;
&lt;li&gt;Browser and server tracking report the same action.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A useful design principle is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Processing the same business event twice should not produce two business conversions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, an idempotency key can combine the booking ID, transition type, and a transition version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;booking-123:confirmed:1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conceptually, the server-side processing logic becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Receive event
    |
Validate event and consent
    |
Check idempotency key
    |
    +-- Already processed --&amp;gt; Do not create another event
    |
    +-- New event --&amp;gt; Store event and enqueue delivery
                              |
                              v
                       Send to platforms
                              |
                              v
                       Record delivery status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The event record and its queue entry should be created reliably together. A retry should reuse the same event identity rather than create a new conversion.&lt;/p&gt;

&lt;p&gt;It is also important to distinguish &lt;strong&gt;accepted by an API&lt;/strong&gt; from &lt;strong&gt;visible in an analytics report&lt;/strong&gt;. Those are separate verification steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Don't confuse bookings with seats
&lt;/h2&gt;

&lt;p&gt;One booking request can cover multiple people.&lt;/p&gt;

&lt;p&gt;Suppose a customer requests three seats and the booking is confirmed.&lt;/p&gt;

&lt;p&gt;That can mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One booking request.&lt;/li&gt;
&lt;li&gt;One confirmed booking.&lt;/li&gt;
&lt;li&gt;Three confirmed seats.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the dashboard reports three bookings instead of three seats, the result is misleading.&lt;/p&gt;

&lt;p&gt;A correct report needs explicit metric definitions.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;What it counts&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Requests&lt;/td&gt;
&lt;td&gt;Booking requests submitted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confirmed bookings&lt;/td&gt;
&lt;td&gt;Booking records confirmed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confirmed seats&lt;/td&gt;
&lt;td&gt;Places within confirmed bookings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cancelled bookings&lt;/td&gt;
&lt;td&gt;Bookings cancelled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Attendance&lt;/td&gt;
&lt;td&gt;Actual attendance records&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is especially important for events, appointments, group activities, and other businesses where one customer action can represent several participants.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Respect consent and attribution limits
&lt;/h2&gt;

&lt;p&gt;Campaign parameters are useful, but they should only be processed and forwarded to analytics platforms when the applicable consent requirements are satisfied.&lt;/p&gt;

&lt;p&gt;In this implementation, consent is part of the tracking flow rather than an afterthought.&lt;/p&gt;

&lt;p&gt;There is also a difference between knowing the campaign associated with a booking request and identifying the exact Instagram conversation that produced it.&lt;/p&gt;

&lt;p&gt;Those are different levels of attribution.&lt;/p&gt;

&lt;p&gt;The current system can preserve available campaign information and report booking statuses. It does &lt;strong&gt;not&lt;/strong&gt; establish a verified one-to-one match between every individual Instagram Direct conversation and a specific booking.&lt;/p&gt;

&lt;p&gt;Reporting should communicate that boundary instead of implying a level of certainty that the data cannot support.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Test the business workflow, not just the tag
&lt;/h2&gt;

&lt;p&gt;A successful network request does not prove that the overall tracking implementation is correct.&lt;/p&gt;

&lt;p&gt;Testing should cover the complete path from an initial booking request to the final business status.&lt;/p&gt;

&lt;p&gt;My checklist includes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a test booking request.&lt;/li&gt;
&lt;li&gt;Verify that a booking record is created.&lt;/li&gt;
&lt;li&gt;Confirm that the correct source information is retained when permitted.&lt;/li&gt;
&lt;li&gt;Check that request and confirmation events are separate.&lt;/li&gt;
&lt;li&gt;Confirm a booking containing multiple seats.&lt;/li&gt;
&lt;li&gt;Replay the same event and verify duplicate protection.&lt;/li&gt;
&lt;li&gt;Test cancellation or another status change.&lt;/li&gt;
&lt;li&gt;Check the queue and destination API responses.&lt;/li&gt;
&lt;li&gt;Verify reporting independently of API acceptance.&lt;/li&gt;
&lt;li&gt;Repeat relevant tests with consent declined.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Test records must also be distinguishable from real business results.&lt;/p&gt;

&lt;p&gt;In the real project, some screenshots and counts come from integration tests. They demonstrate configured workflows, not proven increases in customers, sales, or revenue.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. The lesson: measure verified states
&lt;/h2&gt;

&lt;p&gt;A useful conversion-tracking system is not simply one that sends more events.&lt;/p&gt;

&lt;p&gt;It is one that answers precise questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What actually happened?&lt;/li&gt;
&lt;li&gt;Which system is authoritative for that outcome?&lt;/li&gt;
&lt;li&gt;How do we prevent duplicate reporting?&lt;/li&gt;
&lt;li&gt;Which attribution details are known?&lt;/li&gt;
&lt;li&gt;Which details are still unknown?&lt;/li&gt;
&lt;li&gt;Can the reported result be traced back to a business record?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When these questions are answered, analytics becomes much more useful for operational decisions.&lt;/p&gt;

&lt;p&gt;I documented the real project, including implementation screenshots and its current limitations, in this &lt;a href="https://sergeyboyko.com/work/dva-stola" rel="noopener noreferrer"&gt;booking attribution case study&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The broader principle applies beyond bookings: &lt;strong&gt;track confirmed business outcomes as distinct states, and test the entire data path before trusting a conversion report.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;AI disclosure: AI tools were used to assist with drafting and structuring this article. The content was subsequently reviewed and edited by the author based on practical project experience.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>testing</category>
      <category>analytics</category>
    </item>
  </channel>
</rss>
