Skip to main content

Setup of Webhook

Why use webhooks

When you integrate the Zinsli API, you might want your applications to receive events as they occur in your Zinsli account, so that your backend systems can execute actions accordingly.

To enable webhook events, you need to register webhook endpoints. After you register them, Zinsli can push real-time event data to your application’s webhook endpoint when events happen in your Zinsli account.

Receiving webhook events is particularly useful for listening to events such as when a tenant has chosen one of your products and completed the onboarding process, or if a property manager and tenant agree to close an active rental deposit product.

tip

While the use of webhooks is optional, they provide a more efficient way to receive real-time updates. Alternatively, you can implement polling at regular intervals to check for rental deposits that require your attention.

Set up webhooks

Webhooks can be set up on the platform https://app.zinsli.com. Follow the instructions to create an account and organization.

Choose your Organization and go to "Notifications" in the "My Products & Deposits" section.

  • HTTPS is mandatory for the endpoint.
  • You need to provide a Secret so we can create a JWT signed token. We send the token in the HTTPS header Authorization: Bearer <Token>.
  • You can create up to two endpoints
  • The 'audience' of the webhook will be set to the domain (not full path) which you set in the 'endpoint' field of the notification settings.

Create a handler

Set up an HTTP or HTTPS endpoint function that can accept webhook requests via the POST method.
The webhook must return a successful status code (200) within 5 seconds, so you should respond before executing any complex logic that could cause a timeout.

tip

For developing your endpoint function on your local machine, we recommend using ngrok for webhook testing.

Secure your endpoint

You need to secure your integration by ensuring your handler verifies that all webhook requests are generated by Zinsli. The jwt is signed with HS256. We recommend using one of the libraries listed at jwt.io/libraries to verify the token sent in the http Header
Authorization: Bearer <Token>.

Example NodeJs

const jwt = req.headers?.authorization?.split(' ')[1]

const {payload, protectedHeader} = await jose.jwtVerify(jwt, secret, {
issuer: 'https://api.zinsli.com',
})
Payload object

After verifying the Bearer token you can find the webhook data inside the Bearer tokens payload (not in the body of the request).

interface Event {
collateralId: string
state: 'tenant-invited' | 'closing-settle-claim-by-invoice'
}

Example PHP

To verify a JWT in PHP, you can use the popular firebase/php-jwt library:

require 'vendor/autoload.php';

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

try {
// Get the JWT from the Authorization header
$authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
if (strpos($authHeader, 'Bearer ') === 0) {
$jwt = substr($authHeader, 7); // Remove "Bearer " prefix
} else {
throw new Exception("Invalid authorization header");
}

// Define the secret and expected claims
$secret = 'your-secret-key';
$expectedIssuer = 'urn:example:issuer';
$expectedAudience = 'urn:example:audience';

// Decode and verify the JWT
$decoded = JWT::decode($jwt, new Key($secret, 'HS256')); // HS256 for HMAC-SHA256

// Check issuer and audience manually (optional but recommended)
if ($decoded->iss !== $expectedIssuer || $decoded->aud !== $expectedAudience) {
throw new Exception("Invalid issuer or audience");
}

// Successfully verified
echo "JWT is valid!";
print_r($decoded); // Print the payload

} catch (Exception $e) {
// Handle invalid JWT or errors
echo "JWT verification failed: " . $e->getMessage();
}

Retry Logic

Zinsli will resend a webhook if it does not receive an OK (200) from the receiving end within 120s. Subsequently, notifications will be resent with increasing delays. If it fails for more than 10 times (ca. 17h), the call is abandoned. (Zinsli is notified and will do a followup with intended recipient).

warning

You MUST answer every webhook in order to avoid retries. Even if you don't use / process the information that it conveys.