Skip to main content

Webhooks

Webhooks let Gomry notify your own systems the moment something happens in your organization — a ticket is purchased, a subscription changes, or a form is submitted. Instead of polling the API, you register one or more HTTPS URLs and Gomry sends an HTTP POST request with a JSON payload to each of them as events occur.

Configuring webhook URLs

Webhook endpoints are configured per organization.
1

Open your integration settings

Go to Organization Settings → Integrations in your Gomry dashboard.
2

Add your endpoint URLs

Enter one or more endpoint URLs, one per line. Every URL you add receives a copy of every webhook event for your organization.
3

Save

Save your settings. New events will start being delivered to your endpoints.
Use an https:// endpoint that is publicly reachable and responds quickly (see Delivery & reliability). Webhooks are scoped to the whole organization — there is currently no per-event subscription.

How delivery works

Each event is sent as a separate HTTP request to every configured URL:
PropertyValue
MethodPOST
Content-Typeapplication/json
BodyA single JSON event object (see Payload)
Timeout5 seconds
RetriesNone — delivery is best-effort
Your endpoint should respond with a 2xx status code as quickly as possible. Do any heavy processing after acknowledging the request (for example, by pushing the payload onto a queue), so you don’t hit the delivery timeout.
Webhook payloads are not currently signed, and there is no shared secret or signature header on this delivery path. Treat the data as advisory: keep your endpoint URL private, accept requests only over HTTPS, and confirm anything sensitive (such as payment amounts) against the Payments API using the payment.id from the payload before acting on it.

Event types

The type field identifies what happened. The most common event is payment.succeeded, which fires when an attendee buys a ticket.
typeWhen it fires
payment.succeededA one-time payment completes — most commonly a ticket purchase. payment.billing_reason is one_time_payment.
subscription.createdA recurring subscription is created. payment.billing_reason is subscription_create.
subscription.deletedA subscription is cancelled or ends.
form_response.createdA form / application is submitted for the first time.
form_response.updatedAn existing form / application response is updated (e.g. status change).
Some legacy form-response payloads omit the type field but always include formID and applicationID. Use the presence of those fields to identify a form-response event.

Payload structure

All events share one envelope. Fields are populated based on the event type, so most are optional — always guard for missing values.
FieldTypeDescription
typestringThe event type (see table above).
spaceIDstringThe space the event belongs to, when applicable.
organizationIDstringThe organization the event belongs to.
datestring (ISO 8601)When the event occurred.
userobjectThe person who triggered the event (see below).
paymentobjectPayment details — present on payment.* and subscription.* events.
subscriptionobjectSubscription details — present on subscription.* events.
lineItemsarrayLine items for the payment. Ticket line items have type: "ticket".
formIDstringThe form ID — present on form_response.* events.
applicationIDstringThe application/response ID — present on form_response.* events.
submission_answersobjectSubmitted answers keyed by question — present on form_response.* events.
responseStatusstringPending, Accepted, Rejected, Draft, or Deleted — form responses.
isNewApplicationbooleanWhether this is the first submission — form responses.

user object

FieldTypeDescription
idstringThe user’s ID.
contactIDstringThe contact record ID in your organization.
namestringFull name.
firstName / lastNamestringName parts (populated on form-response events).
emailstringEmail address.
phoneNumberstringPhone number.
linkedin, instagram, github, twitter, websitestringSocial / web profiles, when provided.
biostringShort biography, when provided.
locationstring | objectLocation, when provided.
countrystringCountry, when provided.

payment object

FieldTypeDescription
idstringThe payment ID. Use this to look up the payment via the API.
grossAmountnumberTotal amount charged, in the smallest currency unit.
netAmountnumberAmount after fees, in the smallest currency unit.
currencystringISO 4217 currency code (e.g. usd).
productIDstringThe product ID.
priceIDstringThe price ID.
billing_reasonstringone_time_payment, subscription_create, or subscription_cycle.

Example: ticket purchase

When an attendee buys a ticket, your endpoint receives a payment.succeeded event:
{
  "type": "payment.succeeded",
  "spaceID": "AbCdEfGhIjKlMnOpQrSt",
  "user": {
    "id": "user_123",
    "contactID": "contact_456",
    "name": "Jordan Rivera",
    "email": "jordan@example.com",
    "phoneNumber": "+15551234567",
    "linkedin": "https://linkedin.com/in/jordanrivera",
    "country": "US"
  },
  "date": "2026-06-11T18:30:00.000Z",
  "payment": {
    "id": "pay_789",
    "grossAmount": 5000,
    "netAmount": 4650,
    "currency": "usd",
    "productID": "prod_abc",
    "priceID": "price_def",
    "billing_reason": "one_time_payment"
  },
  "lineItems": [
    {
      "type": "ticket",
      "ticket_owner_email": "jordan@example.com",
      "quantity": 1
    }
  ]
}

Example: form submission

{
  "type": "form_response.created",
  "formID": "form_123",
  "applicationID": "app_456",
  "user": {
    "id": "user_123",
    "contactID": "contact_456",
    "name": "Jordan Rivera",
    "firstName": "Jordan",
    "lastName": "Rivera",
    "email": "jordan@example.com"
  },
  "submission_answers": {
    "What's your company?": "Acme Inc.",
    "Dietary restrictions": "Vegetarian"
  },
  "responseStatus": "Pending",
  "isNewApplication": true,
  "date": "2026-06-11T18:30:00.000Z"
}

Handling webhooks

import express from "express";

const app = express();
app.use(express.json());

app.post("/webhooks/gomry", (req, res) => {
  const event = req.body;

  // Acknowledge immediately, then process asynchronously.
  res.status(200).send("ok");

  switch (event.type) {
    case "payment.succeeded":
      // A ticket was purchased — event.payment.id, event.user.email, …
      handleTicketPurchase(event);
      break;
    case "form_response.created":
    case "form_response.updated":
      handleFormResponse(event);
      break;
    default:
      // form-response events may arrive without a `type`
      if (event.formID) handleFormResponse(event);
  }
});

app.listen(3000);

Best practices

Return a 2xx within the 5-second timeout. Offload anything slow (emails, database writes, third-party calls) to a background job or queue.
Because there are no delivery guarantees and multiple endpoints can be configured, design your handler so processing the same event twice is safe — key off payment.id or applicationID.
Payloads are unsigned. Before acting on money-related data, re-fetch it from the Payments API using the payment.id from the payload.
Most fields are optional and vary by event type. Always check for existence before reading nested values.