Asynchronous
Last updated
Last updated
Extensions used for responding to platform events are run asynchronous. In this section we list the various event payloads as well as show how to configure them.
Asynchronous extensions are configured via a rule on the extension itself. You can select from the following categories:
Event Types
All - triggers upon all event types (implicit - same as leaving blank)
API - triggers upon API calls
Data - triggers upon input or claims capture (username, OTP & federation)
Database - triggers upon resource changes (create, update & delete)
Authentication - triggers upon authentication events (pending, success & failed)
Authorization - triggers upon authorization events (pending, success & failed)
Communication - triggers upon communication requests (send OTP)
Event Results
All - triggers upon all event results (implicit - same as leaving blank)
Success - triggers upon all successful events
Failed - triggers upon all failed events
Pending - triggers upon all pending events
Event Actions
All - triggers upon all event actions (implicit - same as leaving blank)
... (see event details below)
Event Reasons (only relevant for failed or pending events)
All - triggers upon all event reasons (implicit - same as leaving blank)
Combining from multiple categories resembles AND
logic, selecting from multiple within a category resembles OR
logic. For example you could achieve the following logic which triggers your extension when the event type is either an API call or authentication event, and the result is failed (so failed API request or failed authentication attempts).
if ((event.type === 'API' || event.type === 'AUTHENTICATION') &&
(event.result === 'FAILED')) {
extension.execute(event)
}
// triggered on GET /factors
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "get-login|get-signup",
"account_id": "<account_id>", // only if called with token
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|INTERNAL_ERROR", // only if failed
"detail": {
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /factors/login or /factors/signup
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "post-login|post-signup",
"account_id": "<account_id>", // only if succesfull login/signup or called with token
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "body|body[].length|body[x].id", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
"deprecated": true // only if signup without session as this API call is deprecated
}
}
// triggered on GET or POST /factors/oauth2/callback
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "get-oauth2-callback|post-oauth2-callback",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "query.?", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on GET /jwks
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "get-jwks",
"tenant_id": "<tenant_id>",
"result": "SUCCESS|FAILED",
"reason": "INTERNAL_ERROR", // only if failed
"detail": {
"error_id": "<error_id>" // only if internal error
}
}
// triggered on GET /.well-known/openid-configuration
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "get-oidc1-config",
"tenant_id": "<tenant_id>",
"result": "SUCCESS|FAILED",
"reason": "INTERNAL_ERROR", // only if failed
"detail": {
"error_id": "<error_id>" // only if internal error
}
}
// triggered on GET /oauth2/authorize
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "get-oauth2-authorize",
"tenant_id": "<tenant_id>",
"account_id": "<client_id>", // note client ID
"result": "SUCCESS|FAILED",
"reason": "MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "query.?", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /oauth2/token
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "post-oauth2-token",
"tenant_id": "<tenant_id>",
"account_id": "<client_id>", // note client ID
"result": "SUCCESS|FAILED",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR|MISSING_CONSENT|REJECTED_CONSENT|INSUFFICIENT_SCORE", // only if failed
"detail": {
"parameter": "body|body.?|header.?", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on GET /oidc1/userinfo
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "get-oidc1-user-info",
"tenant_id": "<tenant_id>",
"account_id": "<account_id>",
"result": "SUCCESS|FAILED",
"reason": "UNAUTHORIZED|INTERNAL_ERROR", // only if failed
"detail": {
"error_id": "<error_id>" // only if internal error
}
}
// triggered on GET /controls
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "get-controls",
"tenant_id": "<tenant_id>",
"account_id": "<account_id>", // only if called with token
"result": "SUCCESS|FAILED",
"reason": "MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "query.client_id", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /controls
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "post-controls",
"tenant_id": "<tenant_id>",
"account_id": "<account_id>", // only if called with token
"result": "SUCCESS|FAILED",
"reason": "MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "query.client_id|body|body[].length|body[x].id|body[x].expires_in", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on GET /controls/consent [DEPRECATED]
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "get-consent",
"tenant_id": "<tenant_id>",
"account_id": "<account_id>",
"result": "SUCCESS|FAILED",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "query.?", // only if missing or invalid parameter
"error_id": "<error_id>", // only if internal error
"deprecated": true // indicates that this API call is deprecated
}
}
// triggered on POST /controls/consent [DEPRECATED]
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "post-consent",
"tenant_id": "<tenant_id>",
"account_id": "<account_id>",
"result": "SUCCESS|FAILED",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "body|body.[].length|body.[x].?", // only if missing or invalid parameter
"error_id": "<error_id>", // only if internal error
"deprecated": true // indicates that this API call is deprecated
}
}
// triggered on POST /graphql (createFactor)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "create-factor",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "body.input|body.input.?", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (deleteFactor or deleteEnrollment)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "delete-factor|delete-enrollment",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "body.input|body.input.?", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (getFactor or getEnrollment)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "get-factor|get-enrollment",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "body.id", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (listFactors or listEnrollments)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "list-factors|list-enrollments",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|INTERNAL_ERROR", // only if failed
"detail": {
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (updateFactor or updateEnrollment)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "update-factor|update-enrollment",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "body.input|body.input.?", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (createControl, createPermission or createRule)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "create-control|create-permission|create-rule",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "body.input|body.input.?", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (deleteControl, deleteConsent, deletePermission or deleteRule))
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "delete-control|delete-consent|delete-permission|delete-rule",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "body.input|body.input.?", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (getControl, getConsent, getPermission or getRule)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "get-control|get-consent|get-permission|get-rule",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "body.id", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (listControls, listConsents, listPermissions or listRules)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "list-controls|list-consents|list-permissions|list-rules",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|INTERNAL_ERROR", // only if failed
"detail": {
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (updateControl, updateConsent, updatePermission or updateRule)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "update-control|update-consent|update-permission|update-rule",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "body.input|body.input.?", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (createExtension)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "create-extension",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR|QUOTA_REACHED", // only if failed
"detail": {
"parameter": "body.input|body.input.?", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (deleteExtension)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "delete-extension",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "body.input|body.input.?", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (getExtension)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "get-extension",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "body.id", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (listExtensions)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "list-extensions",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|INTERNAL_ERROR", // only if failed
"detail": {
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (updateExtension)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "update-extension",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR|QUOTA_REACHED", // only if failed
"detail": {
"parameter": "body.input|body.input.?", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (createAccount)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "create-account",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "body.input|body.input.?", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (deleteAccount)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "delete-account",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "body.input|body.input.?", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (getAccount)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "get-account",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"parameter": "body.id", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (listAccounts)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "list-accounts",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|INTERNAL_ERROR", // only if failed
"detail": {
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (updateAccount)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "update-account",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|MISSING_PARAMETER|INVALID_PARAMETER|INTERNAL_ERROR|QUOTA_REACHED", // only if failed
"detail": {
"parameter": "body.input|body.input.?", // only if missing or invalid parameter
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (deleteTenant or updateTenant)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "delete-tenant|update-tenant",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|INVALID_PARAMETER|INTERNAL_ERROR|FORBIDDEN", // only if failed
"detail": {
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (getTenant, listTenants or createTenant)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "get-tenant|list-tenants|create-tenant",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|INVALID_PARAMETER|INTERNAL_ERROR", // only if failed
"detail": {
"error_id": "<error_id>" // only if internal error
}
}
// triggered on POST /graphql (listEvents)
{
"type": "API",
"origin": "<origin>", // request origin header
"action": "list-events",
"account_id": "<account_id>",
"tenant_id": "<tenant_id>",
"result": "FAILED|SUCCESS",
"reason": "UNAUTHORIZED|INTERNAL_ERROR", // only if failed
"detail": {
"error_id": "<error_id>" // only if internal error
}
}
// triggered upon factor/enrollment creation, update or deletion
{
"type": "DATABASE",
"origin": "<factor_id|enrollment_id>",
"action": "create-factor|update-factor|delete-factor|create-enrollment|update-enrollment|delete-enrollment",
"account_id": "<account_id>", // only if enrollment
"tenant_id": "<tenant_id>",
"result": "SUCCESS",
"detail": { ... } // full if create or delete, differential if update
}
The following fields get redacted for factors at tenant level (replaced by <REDACTED>
):
label
(if enrollment)
config.secret
(if present)
config.client_secret
(if present on create or delete)
config.client_secret__added
(if present on update)
config.client_secret__deleted
(if present on update)
// triggered upon control/consent/permission/rule creation, update or deletion
{
"type": "DATABASE",
"origin": "<control_id|consent_id|permission_id>",
"action": "create-control|update-control|delete-control|create-consent|update-consent|delete-consent|create-permission|update-permission|delete-permission|create-rule|update-rule|delete-rule",
"account_id": "<account_id>", // only if consent or permission
"tenant_id": "<tenant_id>",
"result": "SUCCESS",
"detail": { ... } // full if create or delete, differential if update
}
The following fields get redacted for controls at tenant level (replaced by <REDACTED>
):
label
(if consent)
label__added
(if consent and present on update)
label__deleted
(if consent and present on update)
// triggered upon account creation, update or deletion
{
"type": "DATABASE",
"origin": "<account_id>",
"action": "create-account|update-account|delete-account",
"account_id": "<account_id>", // same as origin
"tenant_id": "<tenant_id>",
"result": "SUCCESS",
"detail": { ... } // full if create or delete, differential if update
}
// triggered upon extension creation, update or deletion
{
"type": "DATABASE",
"origin": "<extension_id>",
"action": "create-extension|update-extension|delete-extension",
"tenant_id": "<tenant_id>",
"result": "SUCCESS",
"detail": { ... } // full if create or delete, differential if update
}
// triggered upon username or OTP signup (with input capture enabled)
{
"type": "DATA",
"origin": "<factor_id>",
"action": "capture-input",
"tenant_id": "<tenant_id>",
"account_id": "<account_id>",
"result": "SUCCESS",
"detail": {
"enrollment_id": "<enrollment_id>",
"enrollment_status": "<enrollment_status>"
},
"values": { // this element does not get logged, hence not visible under events
"input": "<input>" // input in clear
}
}
// triggered upon OAuth 2.0 login or signup (with claims capture enabled)
{
"type": "DATA",
"origin": "<factor_id>",
"action": "capture-claims",
"tenant_id": "<tenant_id>",
"account_id": "<account_id>",
"result": "SUCCESS",
"detail": {
"provider": "<factor_label>",
"enrollment_id": "<enrollment_id>"
},
"values": { // this element does not get logged, hence not visible under events
... // claims of ID token
}
}
// triggered upon OAuth 2.0 login or signup (with tokens capture enabled)
{
"type": "DATA",
"origin": "<factor_id>",
"action": "capture-tokens",
"tenant_id": "<tenant_id>",
"account_id": "<account_id>",
"result": "SUCCESS",
"detail": {
"provider": "<factor_label>",
"enrollment_id": "<enrollment_id>"
},
"values": { // this element does not get logged, hence not visible under events
"id_token": "<id_token>", // only if present (JWT)
"access_token": "<access_token>" // only if present (unknown format)
}
}
The capture_tokens
feature is currently still experimental and should not be used for production setups — tokens in payload can not be guaranteed to be present at this time.
// triggered upon factor enrollment (initial call during signup if PENDING)
{
"type": "AUTHENTICATION",
"origin": "<factor_id>",
"action": "enroll-factor",
"tenant_id": "<tenant_id>",
"account_id": "<account_id>", // could be missing in some OAuth 2.0 failed flows
"result": "PENDING|SUCCESS|FAILED",
"reason": "ENROLLMENT_PENDING|MISSING_INPUT|INVALID_INPUT|RESERVED_INPUT|ENROLLMENT_ALREADY_EXISTS|INTERNAL_ERROR", // only if pending or failed
"detail": {
"enrollment_id": "<enrollment_id>", // only if not failed
"error_id": "<error_id>" // only if internal error with OAuth 2.0 callback processing
}
}
// triggered upon factor validation (login or secondary call during signup if PENDING)
{
"type": "AUTHENTICATION",
"origin": "<factor_id>",
"action": "validate-factor",
"tenant_id": "<tenant_id>",
"account_id": "<account_id>", // only if known
"result": "PENDING|SUCCESS|FAILED",
"reason": "OTP_PENDING|OAUTH2_PENDING|MISSING_INPUT|INVALID_INPUT|ENROLLMENT_NOT_FOUND|INCORRECT_INPUT|REJECTED_LOGIN|RACING_CONDITION|ENROLLMENT_MISMATCH", // only if pending or failed
"detail": {
"enrollment_id": "<enrollment_id>", // only if known
"error_id": "<error_id>" // only if failed with OAuth 2.0 callback processing
}
}
// triggered upon client access (post controls or get consent)
{
"type": "AUTHORIZATION",
"origin": "<client_id>",
"action": "access-client",
"tenant_id": "<tenant_id>",
"account_id": "<account_id>",
"result": "PENDING|SUCCESS|FAILED"
}
// triggered upon OTP validation (login or signup)
{
"type": "COMMUNICATION",
"origin": "<factor_id>",
"action": "send-otp",
"tenant_id": "<tenant_id>",
"account_id": "<account_id>",
"result": "PENDING",
"reason": "DELIVERY_PENDING",
"detail": {
"enrollment_id": "<enrollment_id>",
"expires_at": "ISO timestamp" // OTP expiration
},
"values": { // this element does not get logged, hence not visible under events
"otp": "<otp>", // OTP in clear
"input": "<input>" // input in clear, only if signup and capture_input=true
}
}
// triggered when account is expiring in 30 days
{
"type": "COMMUNICATION",
"origin": "<account_id>",
"action": "warn-account",
"tenant_id": "<tenant_id>",
"account_id": "<account_id>",
"result": "PENDING",
"reason": "DELIVERY_PENDING",
"detail": {
"expires_at": "ISO timestamp" // account expiration
}
}
// triggered when tenant is expiring in 30 days
{
"type": "COMMUNICATION",
"origin": "<tenant_id>",
"action": "warn-tenant",
"tenant_id": "<tenant_id>",
"result": "PENDING",
"reason": "DELIVERY_PENDING",
"detail": {
"expires_at": "ISO timestamp", // tenant expiration
"admin_client": "<client_id>" // client ID of admin UI
}
}
An example that pushes the OTP to a public AWS Lambda endpoint to deliver to OTP to the user by email using Amazon Simple Email Service (SES).
exports.handler = async function(event) {
return fetch('https://xxxx.lambda-url.eu-west-1.on.aws/', {
method: 'POST',
body: JSON.stringify(event)
});
}
The code for the receiving Lambda below.
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
var aws = require("aws-sdk");
var ses = new aws.SES({});
exports.handler = async function (event) {
const request = JSON.parse(event.body);
var params = {
Destination: {
// input field is only available at signup, hence you should store this field
// with a mapping to the Quasr account ID, and on subsequent calls fetch user
// email using the Quasr account ID
ToAddresses: [ request.values.input ],
},
Message: {
Subject: { Data: "Your OTP" },
Body: {
Text: { Data: `Your OTP is ${request.values.otp}` },
},
},
Source: "chris@darkmattr.io",
};
return ses.sendEmail(params).promise()
};
Once a user logs in using the One-Time-Password, the code gets executed, and as in this example, the OTP delivered to the user (in this example, it's simply sent to the Lambda endpoint without further distinction; in real-world scenarios, these could be requests to send an email or SMS via external APIs for example).
For formatting of detail
claim in case of update
action see documentation. Only the changes (differential) are detailed, not the entire resource. In case you need it you'll have to request it via API.