ErtisAuth

Webhooks

Call your endpoints when something happens.

A webhook sends an HTTP request to a URL of your choice whenever a given event occurs in the membership. Use webhooks to keep other systems in sync with ErtisAuth: create a customer record when a user signs up, clean up data when a user is deleted, post to a chat channel when a role changes.

The webhook object#

json
{
	"_id": "66f1c0d2a4b5c6d7e8f901a0",
	"name": "Sync new users to CRM",
	"slug": "sync-new-users-to-crm",
	"description": "Creates a contact for every new user",
	"event": "UserCreated",
	"status": "active",
	"try_count": 3,
	"request": {
		"method": "POST",
		"url": "https://crm.example.com/hooks/ertisauth/users/{{document._id}}",
		"headers": {
			"Authorization": "Bearer <crm_api_key>",
			"X-Source": "ertisauth"
		},
		"body": {
			"email": "{{document.email_address}}",
			"name": "{{document.firstname}} {{document.lastname}}",
			"source": "signup"
		},
		"uncoveredBody": false
	},
	"membership_id": "66f1c0d2a4b5c6d7e8f90123"
}
FieldRequiredDescription
nameyesDisplay name.
descriptionno
eventyesThe event type that triggers the webhook, e.g. UserCreated.
statusyesactive or passive. Passive webhooks are kept but not called.
try_countyesHow many times a failing request is tried, from 1 to 5.
request.methodyesThe HTTP method: GET, POST, PUT, PATCH, DELETE…
request.urlyesThe URL to call. May contain placeholders.
request.headersnoHeaders to send. Values may contain placeholders.
request.bodynoA JSON object. String values may contain placeholders.
request.uncoveredBodynoSee The request body. false by default.

Placeholders#

The URL, the header values and the string values of the body can contain placeholders in double braces, filled in from the event:

PlaceholderValue
{{event_type}}The event type
{{utilizer_id}}Who caused the event
{{event_time}}When it happened
{{membership_id}}The membership id
{{document.<field>}}A field of the resource after the change, e.g. {{document.email_address}}
{{prior.<field>}}A field of the resource before the change

Nested fields use dots: {{document.sys.created_by}}.

The values are escaped for where they are used, so that the data of an event (a user's name, for example) can't change the request:

  • in the URL, values are URL-encoded (../admin becomes ..%2Fadmin);
  • in headers, a value that would contain a line break is not sent;
  • in the body, each placeholder stays inside its JSON string: it can't add fields or change the structure.

The request body#

By default (uncoveredBody: false) the request body wraps your body together with the event's documents:

json
{
	"document": { "_id": "66f1c0d2a4b5c6d7e8f90127", "username": "ada", "email_address": "ada@example.com", "…": "…" },
	"prior": null,
	"payload": {
		"email": "ada@example.com",
		"name": "Ada Lovelace",
		"source": "signup"
	}
}

With uncoveredBody: true the request body is your body alone, after the placeholders are filled in:

json
{
	"email": "ada@example.com",
	"name": "Ada Lovelace",
	"source": "signup"
}

Use uncoveredBody: true when the receiving API expects a specific format.

Delivery#

  • Webhooks are called asynchronously, from a background queue: the request that caused the event doesn't wait for them, and a slow or failing receiver can't slow down or break ErtisAuth.
  • A call counts as successful when the receiver answers with a 2xx status. Otherwise it is tried again, up to try_count times in total, one right after the other.
  • Every attempt records a WebhookRequestSent or WebhookRequestFailed event with the request, the status code and the response body, so you can see what happened.
  • Webhooks are delivered at most once per attempt, not exactly once: make your receiver idempotent (for example by using document._id), and expect that a call may be missed if ErtisAuth restarts while it is queued.

Securing your receiver#

  • Use HTTPS.
  • Authenticate the calls: put a secret in a header ("Authorization": "Bearer <secret>") or in the URL, and check it in your receiver.
  • Don't trust the body blindly: when in doubt, read the resource from ErtisAuth with an application token.

Endpoints#

All routes are under /memberships/{membershipId}.

MethodRouteDescriptionPermission
GET/webhooks/{id}Get a webhookwebhooks.read.{id}
GET/webhooksList webhookswebhooks.read
POST/webhooks/_queryQuery webhookswebhooks.read
POST/webhooksCreate a webhookwebhooks.create
PUT/webhooks/{id}Update a webhookwebhooks.update.{id}
DELETE/webhooks/{id}Delete a webhookwebhooks.delete.{id}
DELETE/webhooksDelete several webhookswebhooks.delete

Create a webhook#

shell
curl -X POST https://auth.example.com/memberships/<membership_id>/webhooks \
	-H 'Authorization: Bearer <access_token>' \
	-H 'Content-Type: application/json' \
	-d '{
		"name": "Notify on user deletion",
		"event": "UserDeleted",
		"status": "active",
		"try_count": 3,
		"request": {
			"method": "DELETE",
			"url": "https://shop.example.com/api/customers/{{prior._id}}",
			"headers": { "Authorization": "Bearer <shop_api_key>" }
		}
	}'

Response 201 Created: the webhook.

ErrorWhen
400 ModelValidationErrorA required field is missing, the event type or the HTTP method is unknown, or try_count is not between 1 and 5
404 MembershipNotFoundThe membership does not exist
409 WebhookAlreadyExistsThe slug is taken

Update and delete#

PUT /webhooks/{id} replaces the webhook with the body (same fields as create). An update without any change answers 409 IdenticalDocumentError. DELETE /webhooks/{id} answers 204 No Content.

Events#

WebhookCreated, WebhookUpdated, WebhookDeleted, and the delivery events WebhookRequestSent and WebhookRequestFailed. See Events.

Found a mistake in the docs? Open an issue