Webhooks
AccessGrid can send webhook notifications to your server when events occur. Webhooks use the CloudEvents specification for event delivery.
Configure webhooks in your AccessGrid console.
We use simple bearer tokens for authentication, which is generated whenever you create a new webhook. In general if your server responds with either 200, or 201, then we will not send the event occurrence again.
If we cannot reach your server, or you respond with non 200/201 response code, we will try again for up to 6 hours before dropping the delivery attempts.
spec_version
string
id
string
source
string
type
string
data_content_type
string
time
string
data
object
access_pass_id
nullable string
ID of the access pass (for access_pass events)
card_template_id
nullable string
ID of the card template (for card_template events)
landing_page_id
nullable string
ID of the landing page (for landing_page events)
credential_profile_id
nullable string
ID of the credential profile (for credential_profile events)
account_id
nullable string
API ID of the account (for account_balance events)
organization_name
nullable string
Name of the organization (for account_balance events)
current_balance
nullable number
Current balance in dollars (for account_balance events)
threshold
nullable number
Low balance threshold in dollars (for account_balance events)
amount_below_threshold
nullable number
How far below threshold the balance is in dollars (for account_balance events)
protocol
nullable string
Protocol type (desfire, seos, smart_tap)
metadata
nullable object
Custom metadata associated with the resource
device
nullable object
Device information (for access_pass device events)
card_number
nullable string
Card number from credential format, only populated if used directly during issuance
site_code
nullable string
Site code from credential format, only populated if used directly during issuance, otherwise 69
file_data
nullable string
Hex-encoded credential data, only populated if used directly or via credential pools
Request
# Example webhook payload (CloudEvents format)
# This is what AccessGrid will POST to your webhook URL
# Example: Access Pass Issued Event
{
"specversion": "1.0",
"id": "unique-event-id-12345",
"source": "accessgrid",
"type": "ag.access_pass.issued",
"datacontenttype": "application/json",
"time": "2025-01-15T10:30:00Z",
"data": {
"access_pass_id": "0xp455-3x1d",
"protocol": "desfire",
"card_number": "12345",
"site_code": "100",
"file_data": "0A1B2C3D4E5F",
"metadata": {
"custom_field": "value"
}
}
}
# Example: Card Template Published Event
{
"specversion": "1.0",
"id": "unique-event-id-67890",
"source": "accessgrid",
"type": "ag.card_template.published",
"datacontenttype": "application/json",
"time": "2025-01-15T11:00:00Z",
"data": {
"card_template_id": "0xt3mp14t3-3x1d",
"protocol": "seos",
"metadata": {}
}
}
# Verify webhook with your endpoint
curl -X POST https://your-server.com/webhooks \
-H "Content-Type: application/cloudevents+json" \
-H "User-Agent: AccessGrid-Webhooks/1.0" \
-d '{
"specversion": "1.0",
"id": "test-event-123",
"source": "accessgrid",
"type": "ag.access_pass.activated",
"datacontenttype": "application/json",
"time": "2025-01-15T12:00:00Z",
"data": {
"access_pass_id": "0xp455-3x1d",
"protocol": "desfire",
"card_number": "12345",
"site_code": "100",
"file_data": "0A1B2C3D4E5F",
"device": {
"type": "iphone",
"id": "device-hash-id"
}
}
}'
require 'sinatra'
require 'json'
# Webhook endpoint to receive AccessGrid events
post '/webhooks' do
request.body.rewind
payload = JSON.parse(request.body.read)
# Verify it's a CloudEvents payload
unless payload['specversion'] == '1.0'
halt 400, { error: 'Invalid CloudEvents format' }.to_json
end
# Handle different event types
case payload['type']
when 'ag.access_pass.issued'
handle_access_pass_issued(payload['data'])
when 'ag.access_pass.activated'
handle_access_pass_activated(payload['data'])
when 'ag.card_template.published'
handle_template_published(payload['data'])
else
puts "Unknown event type: #{payload['type']}"
end
# Always return 200 to acknowledge receipt
status 200
{ received: true }.to_json
end
def handle_access_pass_issued(data)
puts "Access pass issued: #{data['access_pass_id']}"
# Your custom logic here
end
def handle_access_pass_activated(data)
puts "Access pass activated: #{data['access_pass_id']}"
puts "Device: #{data['device']['type']}"
# Your custom logic here
end
def handle_template_published(data)
puts "Template published: #{data['card_template_id']}"
# Your custom logic here
end
const express = require('express');
const app = express();
app.use(express.json({
type: ['application/json', 'application/cloudevents+json']
}));
// Webhook endpoint to receive AccessGrid events
app.post('/webhooks', (req, res) => {
const payload = req.body;
// Verify it's a CloudEvents payload
if (payload.specversion !== '1.0') {
return res.status(400).json({ error: 'Invalid CloudEvents format' });
}
// Handle different event types
switch (payload.type) {
case 'ag.access_pass.issued':
handleAccessPassIssued(payload.data);
break;
case 'ag.access_pass.activated':
handleAccessPassActivated(payload.data);
break;
case 'ag.card_template.published':
handleTemplatePublished(payload.data);
break;
default:
console.log(`Unknown event type: ${payload.type}`);
}
// Always return 200 to acknowledge receipt
res.status(200).json({ received: true });
});
function handleAccessPassIssued(data) {
console.log(`Access pass issued: ${data.access_pass_id}`);
// Your custom logic here
}
function handleAccessPassActivated(data) {
console.log(`Access pass activated: ${data.access_pass_id}`);
console.log(`Device: ${data.device.type}`);
// Your custom logic here
}
function handleTemplatePublished(data) {
console.log(`Template published: ${data.card_template_id}`);
// Your custom logic here
}
app.listen(3000, () => {
console.log('Webhook server listening on port 3000');
});
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/webhooks', methods=['POST'])
def webhook():
payload = request.get_json()
# Verify it's a CloudEvents payload
if payload.get('specversion') != '1.0':
return jsonify({'error': 'Invalid CloudEvents format'}), 400
# Handle different event types
event_type = payload.get('type')
data = payload.get('data', {})
if event_type == 'ag.access_pass.issued':
handle_access_pass_issued(data)
elif event_type == 'ag.access_pass.activated':
handle_access_pass_activated(data)
elif event_type == 'ag.card_template.published':
handle_template_published(data)
else:
print(f"Unknown event type: {event_type}")
# Always return 200 to acknowledge receipt
return jsonify({'received': True}), 200
def handle_access_pass_issued(data):
print(f"Access pass issued: {data['access_pass_id']}")
# Your custom logic here
def handle_access_pass_activated(data):
print(f"Access pass activated: {data['access_pass_id']}")
print(f"Device: {data['device']['type']}")
# Your custom logic here
def handle_template_published(data):
print(f"Template published: {data['card_template_id']}")
# Your custom logic here
if __name__ == '__main__':
app.run(port=3000)
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
type CloudEvent struct {
SpecVersion string `json:"specversion"`
ID string `json:"id"`
Source string `json:"source"`
Type string `json:"type"`
Time string `json:"time"`
Data map[string]interface{} `json:"data"`
}
func webhookHandler(w http.ResponseWriter, r *http.Request) {
var event CloudEvent
if err := json.NewDecoder(r.Body).Decode(&event); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
// Verify it's a CloudEvents payload
if event.SpecVersion != "1.0" {
http.Error(w, "Invalid CloudEvents format", http.StatusBadRequest)
return
}
// Handle different event types
switch event.Type {
case "ag.access_pass.issued":
handleAccessPassIssued(event.Data)
case "ag.access_pass.activated":
handleAccessPassActivated(event.Data)
case "ag.card_template.published":
handleTemplatePublished(event.Data)
default:
log.Printf("Unknown event type: %s", event.Type)
}
// Always return 200 to acknowledge receipt
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]bool{"received": true})
}
func handleAccessPassIssued(data map[string]interface{}) {
fmt.Printf("Access pass issued: %v\n", data["access_pass_id"])
// Your custom logic here
}
func handleAccessPassActivated(data map[string]interface{}) {
fmt.Printf("Access pass activated: %v\n", data["access_pass_id"])
if device, ok := data["device"].(map[string]interface{}); ok {
fmt.Printf("Device: %v\n", device["type"])
}
// Your custom logic here
}
func handleTemplatePublished(data map[string]interface{}) {
fmt.Printf("Template published: %v\n", data["card_template_id"])
// Your custom logic here
}
func main() {
http.HandleFunc("/webhooks", webhookHandler)
log.Println("Webhook server listening on port 3000")
log.Fatal(http.ListenAndServe(":3000", nil))
}
using System;
using System.IO;
using System.Text.Json;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/webhooks", async (HttpContext context) =>
{
using var reader = new StreamReader(context.Request.Body);
var body = await reader.ReadToEndAsync();
var payload = JsonSerializer.Deserialize<CloudEvent>(body);
// Verify it's a CloudEvents payload
if (payload?.SpecVersion != "1.0")
{
context.Response.StatusCode = 400;
await context.Response.WriteAsJsonAsync(new { error = "Invalid CloudEvents format" });
return;
}
// Handle different event types
switch (payload.Type)
{
case "ag.access_pass.issued":
HandleAccessPassIssued(payload.Data);
break;
case "ag.access_pass.activated":
HandleAccessPassActivated(payload.Data);
break;
case "ag.card_template.published":
HandleTemplatePublished(payload.Data);
break;
default:
Console.WriteLine($"Unknown event type: {payload.Type}");
break;
}
// Always return 200 to acknowledge receipt
await context.Response.WriteAsJsonAsync(new { received = true });
});
void HandleAccessPassIssued(JsonElement data)
{
Console.WriteLine($"Access pass issued: {data.GetProperty("access_pass_id").GetString()}");
// Your custom logic here
}
void HandleAccessPassActivated(JsonElement data)
{
Console.WriteLine($"Access pass activated: {data.GetProperty("access_pass_id").GetString()}");
if (data.TryGetProperty("device", out var device))
{
Console.WriteLine($"Device: {device.GetProperty("type").GetString()}");
}
// Your custom logic here
}
void HandleTemplatePublished(JsonElement data)
{
Console.WriteLine($"Template published: {data.GetProperty("card_template_id").GetString()}");
// Your custom logic here
}
app.Run("http://localhost:3000");
record CloudEvent(string SpecVersion, string Id, string Source, string Type, string Time, JsonElement Data);
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpExchange;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import java.io.*;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
public class WebhookServer {
private static final Gson gson = new Gson();
public static void main(String[] args) throws IOException {
HttpServer server = HttpServer.create(new InetSocketAddress(3000), 0);
server.createContext("/webhooks", new WebhookHandler());
server.start();
System.out.println("Webhook server listening on port 3000");
}
static class WebhookHandler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
if (!"POST".equals(exchange.getRequestMethod())) {
exchange.sendResponseHeaders(405, 0);
exchange.close();
return;
}
InputStream is = exchange.getRequestBody();
String body = new String(is.readAllBytes(), StandardCharsets.UTF_8);
JsonObject payload = gson.fromJson(body, JsonObject.class);
// Verify it's a CloudEvents payload
if (!"1.0".equals(payload.get("specversion").getAsString())) {
String response = "{\"error\": \"Invalid CloudEvents format\"}";
exchange.sendResponseHeaders(400, response.length());
OutputStream os = exchange.getResponseBody();
os.write(response.getBytes());
os.close();
return;
}
// Handle different event types
String type = payload.get("type").getAsString();
JsonObject data = payload.getAsJsonObject("data");
switch (type) {
case "ag.access_pass.issued":
handleAccessPassIssued(data);
break;
case "ag.access_pass.activated":
handleAccessPassActivated(data);
break;
case "ag.card_template.published":
handleTemplatePublished(data);
break;
default:
System.out.println("Unknown event type: " + type);
}
// Always return 200 to acknowledge receipt
String response = "{\"received\": true}";
exchange.sendResponseHeaders(200, response.length());
OutputStream os = exchange.getResponseBody();
os.write(response.getBytes());
os.close();
}
private void handleAccessPassIssued(JsonObject data) {
System.out.println("Access pass issued: " + data.get("access_pass_id").getAsString());
// Your custom logic here
}
private void handleAccessPassActivated(JsonObject data) {
System.out.println("Access pass activated: " + data.get("access_pass_id").getAsString());
if (data.has("device")) {
JsonObject device = data.getAsJsonObject("device");
System.out.println("Device: " + device.get("type").getAsString());
}
// Your custom logic here
}
private void handleTemplatePublished(JsonObject data) {
System.out.println("Template published: " + data.get("card_template_id").getAsString());
// Your custom logic here
}
}
}
<?php
require 'vendor/autoload.php';
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
$app = AppFactory::create();
$app->addBodyParsingMiddleware();
$app->post('/webhooks', function (Request $request, Response $response) {
$payload = $request->getParsedBody();
// Verify it's a CloudEvents payload
if (!isset($payload['specversion']) || $payload['specversion'] !== '1.0') {
$response->getBody()->write(json_encode(['error' => 'Invalid CloudEvents format']));
return $response->withStatus(400)->withHeader('Content-Type', 'application/json');
}
// Handle different event types
$type = $payload['type'] ?? '';
$data = $payload['data'] ?? [];
switch ($type) {
case 'ag.access_pass.issued':
handleAccessPassIssued($data);
break;
case 'ag.access_pass.activated':
handleAccessPassActivated($data);
break;
case 'ag.card_template.published':
handleTemplatePublished($data);
break;
default:
error_log("Unknown event type: $type");
}
// Always return 200 to acknowledge receipt
$response->getBody()->write(json_encode(['received' => true]));
return $response->withHeader('Content-Type', 'application/json');
});
function handleAccessPassIssued($data) {
error_log("Access pass issued: {$data['access_pass_id']}");
// Your custom logic here
}
function handleAccessPassActivated($data) {
error_log("Access pass activated: {$data['access_pass_id']}");
if (isset($data['device'])) {
error_log("Device: {$data['device']['type']}");
}
// Your custom logic here
}
function handleTemplatePublished($data) {
error_log("Template published: {$data['card_template_id']}");
// Your custom logic here
}
$app->run();
defmodule MyAppWeb.WebhookController do
use MyAppWeb, :controller
def handle(conn, params) do
# Verify it's a CloudEvents payload
unless params["specversion"] == "1.0" do
conn
|> put_status(400)
|> json(%{error: "Invalid CloudEvents format"})
|> halt()
end
# Handle different event types
case params["type"] do
"ag.access_pass.issued" ->
handle_access_pass_issued(params["data"])
"ag.access_pass.activated" ->
handle_access_pass_activated(params["data"])
"ag.card_template.published" ->
handle_template_published(params["data"])
type ->
IO.puts("Unknown event type: #{type}")
end
# Always return 200 to acknowledge receipt
json(conn, %{received: true})
end
defp handle_access_pass_issued(data) do
IO.puts("Access pass issued: #{data["access_pass_id"]}")
# Your custom logic here
end
defp handle_access_pass_activated(data) do
IO.puts("Access pass activated: #{data["access_pass_id"]}")
IO.puts("Device: #{data["device"]["type"]}")
# Your custom logic here
end
defp handle_template_published(data) do
IO.puts("Template published: #{data["card_template_id"]}")
# Your custom logic here
end
end
Response
Empty
List Webhooks
Retrieve a paginated list of webhooks configured for your account.
page
nullable integer
per_page
nullable integer
Request
# Build the JSON payload
PAYLOAD="{}"
# Sign the payload
PAYLOAD_B64=$(printf '%s' "$PAYLOAD" | openssl base64 -A)
SIG=$(printf '%s' "$PAYLOAD_B64" | openssl dgst -sha256 -hmac "$SECRET_KEY" -hex | awk '{print $NF}')
curl -G \
-H "X-ACCT-ID: $ACCOUNT_ID" \
-H "X-PAYLOAD-SIG: $SIG" \
--data-urlencode "sig_payload=$PAYLOAD" \
"https://api.accessgrid.com/v1/console/webhooks"
require 'accessgrid'
account_id = ENV['ACCOUNT_ID']
secret_key = ENV['SECRET_KEY']
client = AccessGrid::Client.new(account_id, secret_key)
webhooks = client.console.webhooks.list
webhooks.each do |webhook|
puts "ID: #{webhook.id}, Name: #{webhook.name}"
end
import AccessGrid from 'accessgrid';
const accountId = process.env.ACCOUNT_ID;
const secretKey = process.env.SECRET_KEY;
const client = new AccessGrid(accountId, secretKey);
const listWebhooks = async () => {
try {
const webhooks = await client.console.webhooks.list();
webhooks.forEach((webhook) => {
console.log(`ID: ${webhook.id}, Name: ${webhook.name}`);
});
} catch (error) {
console.error('Error listing webhooks:', error);
}
};
listWebhooks();
from accessgrid import AccessGrid
import os
account_id = os.getenv('ACCOUNT_ID')
secret_key = os.getenv('SECRET_KEY')
client = AccessGrid(account_id, secret_key)
webhooks = client.console.webhooks.list()
for webhook in webhooks:
print(f"ID: {webhook.id}, Name: {webhook.name}")
package main
import (
"context"
"fmt"
"os"
"github.com/Access-Grid/accessgrid-go"
)
func main() {
accountID := os.Getenv("ACCOUNT_ID")
secretKey := os.Getenv("SECRET_KEY")
client, err := accessgrid.NewClient(accountID, secretKey)
if err != nil {
fmt.Printf("Error creating client: %v\n", err)
return
}
ctx := context.Background()
response, err := client.Console.Webhooks.List(ctx)
if err != nil {
fmt.Printf("Error listing webhooks: %v\n", err)
return
}
for _, webhook := range response.Webhooks {
fmt.Printf("ID: %s, Name: %s\n", webhook.ID, webhook.Name)
}
}
using AccessGrid;
using System;
using System.Threading.Tasks;
public async Task ListWebhooksAsync()
{
var accountId = Environment.GetEnvironmentVariable("ACCOUNT_ID");
var secretKey = Environment.GetEnvironmentVariable("SECRET_KEY");
var client = new AccessGridClient(accountId, secretKey);
var response = await client.Console.Webhooks.ListAsync();
foreach (var webhook in response.Webhooks)
{
Console.WriteLine($"ID: {webhook.Id}, Name: {webhook.Name}");
}
}
import com.organization.accessgrid.AccessGridClient;
import com.organization.accessgrid.Models;
import java.util.List;
public class WebhookService {
private final AccessGridClient client;
public WebhookService() {
String accountId = System.getenv("ACCOUNT_ID");
String secretKey = System.getenv("SECRET_KEY");
this.client = new AccessGridClient(accountId, secretKey);
}
public List<Models.Webhook> listWebhooks() {
List<Models.Webhook> webhooks = client.console().webhooks().list();
for (Models.Webhook webhook : webhooks) {
System.out.printf("ID: %s, Name: %s%n", webhook.getId(), webhook.getName());
}
return webhooks;
}
}
<?php
require 'vendor/autoload.php';
use AccessGridClient;
$accountId = $_ENV['ACCOUNT_ID'];
$secretKey = $_ENV['SECRET_KEY'];
$client = new Client($accountId, $secretKey);
$response = $client->console->listWebhooks();
foreach ($response['webhooks'] as $webhook) {
echo "ID: {$webhook->id}, Name: {$webhook->name}\n";
}
client = AccessGrid.Client.new(
account_id: System.get_env("ACCOUNT_ID"),
api_secret: System.get_env("SECRET_KEY")
)
{:ok, webhooks, _pagination} = AccessGrid.Console.list_webhooks(client: client)
Enum.each(webhooks, fn webhook ->
IO.puts("ID: #{webhook.id}, Name: #{webhook.name}")
end)
Response
Empty
Create Webhook
Create a new webhook to receive event notifications. URL must be reachable and at least one event must be subscribed.
name
string
url
string
auth_method
nullable string
subscribed_events
array
Request
# Build the JSON payload
PAYLOAD='{"name":"Production","url":"https://example.com/webhooks","subscribed_events":["ag.access_pass.issued"]}'
# Sign the payload
PAYLOAD_B64=$(printf '%s' "$PAYLOAD" | openssl base64 -A)
SIG=$(printf '%s' "$PAYLOAD_B64" | openssl dgst -sha256 -hmac "$SECRET_KEY" -hex | awk '{print $NF}')
curl -X POST \
-H "X-ACCT-ID: $ACCOUNT_ID" \
-H "X-PAYLOAD-SIG: $SIG" \
-H "Content-Type: application/json" \
-d "$PAYLOAD" \
"https://api.accessgrid.com/v1/console/webhooks"
require 'accessgrid'
account_id = ENV['ACCOUNT_ID']
secret_key = ENV['SECRET_KEY']
client = AccessGrid::Client.new(account_id, secret_key)
webhook = client.console.webhooks.create(
name: 'Production',
url: 'https://example.com/webhooks',
subscribed_events: ['ag.access_pass.issued']
)
puts "Webhook created: #{webhook.id}"
puts "Private key: #{webhook.private_key}"
import AccessGrid from 'accessgrid';
const accountId = process.env.ACCOUNT_ID;
const secretKey = process.env.SECRET_KEY;
const client = new AccessGrid(accountId, secretKey);
const createWebhook = async () => {
try {
const webhook = await client.console.webhooks.create({
name: 'Production',
url: 'https://example.com/webhooks',
subscribedEvents: ['ag.access_pass.issued']
});
console.log(`Webhook created: ${webhook.id}`);
console.log(`Private key: ${webhook.privateKey}`);
} catch (error) {
console.error('Error creating webhook:', error);
}
};
createWebhook();
from accessgrid import AccessGrid
import os
account_id = os.getenv('ACCOUNT_ID')
secret_key = os.getenv('SECRET_KEY')
client = AccessGrid(account_id, secret_key)
webhook = client.console.webhooks.create(
name='Production',
url='https://example.com/webhooks',
subscribed_events=['ag.access_pass.issued']
)
print(f"Webhook created: {webhook.id}")
print(f"Private key: {webhook.private_key}")
package main
import (
"context"
"fmt"
"os"
"github.com/Access-Grid/accessgrid-go"
)
func main() {
accountID := os.Getenv("ACCOUNT_ID")
secretKey := os.Getenv("SECRET_KEY")
client, err := accessgrid.NewClient(accountID, secretKey)
if err != nil {
fmt.Printf("Error creating client: %v\n", err)
return
}
params := accessgrid.CreateWebhookParams{
Name: "Production",
URL: "https://example.com/webhooks",
SubscribedEvents: []string{"ag.access_pass.issued"},
}
ctx := context.Background()
webhook, err := client.Console.Webhooks.Create(ctx, params)
if err != nil {
fmt.Printf("Error creating webhook: %v\n", err)
return
}
fmt.Printf("Webhook created: %s\n", webhook.ID)
fmt.Printf("Private key: %s\n", webhook.PrivateKey)
}
using AccessGrid;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public async Task CreateWebhookAsync()
{
var accountId = Environment.GetEnvironmentVariable("ACCOUNT_ID");
var secretKey = Environment.GetEnvironmentVariable("SECRET_KEY");
var client = new AccessGridClient(accountId, secretKey);
var webhook = await client.Console.Webhooks.CreateAsync(new CreateWebhookRequest
{
Name = "Production",
Url = "https://example.com/webhooks",
SubscribedEvents = new List<string> { "ag.access_pass.issued" }
});
Console.WriteLine($"Webhook created: {webhook.Id}");
Console.WriteLine($"Private key: {webhook.PrivateKey}");
}
import com.organization.accessgrid.AccessGridClient;
import com.organization.accessgrid.Models;
import java.util.List;
public class WebhookService {
private final AccessGridClient client;
public WebhookService() {
String accountId = System.getenv("ACCOUNT_ID");
String secretKey = System.getenv("SECRET_KEY");
this.client = new AccessGridClient(accountId, secretKey);
}
public Models.Webhook createWebhook() {
Models.CreateWebhookRequest request = Models.CreateWebhookRequest.builder()
.name("Production")
.url("https://example.com/webhooks")
.subscribedEvents(List.of("ag.access_pass.issued"))
.build();
Models.Webhook webhook = client.console().webhooks().create(request);
System.out.printf("Webhook created: %s%n", webhook.getId());
System.out.printf("Private key: %s%n", webhook.getPrivateKey());
return webhook;
}
}
<?php
require 'vendor/autoload.php';
use AccessGridClient;
$accountId = $_ENV['ACCOUNT_ID'];
$secretKey = $_ENV['SECRET_KEY'];
$client = new Client($accountId, $secretKey);
$webhook = $client->console->createWebhook([
'name' => 'Production',
'url' => 'https://example.com/webhooks',
'subscribed_events' => ['ag.access_pass.issued']
]);
echo "Webhook created: {$webhook->id}\n";
echo "Private key: {$webhook->privateKey}\n";
client = AccessGrid.Client.new(
account_id: System.get_env("ACCOUNT_ID"),
api_secret: System.get_env("SECRET_KEY")
)
{:ok, webhook} = AccessGrid.Console.create_webhook(
%{
name: "Production",
url: "https://example.com/webhooks",
subscribed_events: ["ag.access_pass.issued"]
},
client: client
)
IO.puts("Webhook created: #{webhook.id}")
IO.puts("Private key: #{webhook.private_key}")
Response
Empty
Delete Webhook
Delete a webhook by ID.
webhook_id
string
Request
# Build the JSON payload
PAYLOAD="{}"
# Sign the payload
PAYLOAD_B64=$(printf '%s' "$PAYLOAD" | openssl base64 -A)
SIG=$(printf '%s' "$PAYLOAD_B64" | openssl dgst -sha256 -hmac "$SECRET_KEY" -hex | awk '{print $NF}')
curl -G \
-X DELETE \
-H "X-ACCT-ID: $ACCOUNT_ID" \
-H "X-PAYLOAD-SIG: $SIG" \
--data-urlencode "sig_payload=$PAYLOAD" \
"https://api.accessgrid.com/v1/console/webhooks/{webhook_id}"
require 'accessgrid'
account_id = ENV['ACCOUNT_ID']
secret_key = ENV['SECRET_KEY']
client = AccessGrid::Client.new(account_id, secret_key)
client.console.webhooks.delete('abc123')
puts "Webhook deleted"
import AccessGrid from 'accessgrid';
const accountId = process.env.ACCOUNT_ID;
const secretKey = process.env.SECRET_KEY;
const client = new AccessGrid(accountId, secretKey);
const deleteWebhook = async () => {
try {
await client.console.webhooks.delete('abc123');
console.log('Webhook deleted');
} catch (error) {
console.error('Error deleting webhook:', error);
}
};
deleteWebhook();
from accessgrid import AccessGrid
import os
account_id = os.getenv('ACCOUNT_ID')
secret_key = os.getenv('SECRET_KEY')
client = AccessGrid(account_id, secret_key)
client.console.webhooks.delete("abc123")
print("Webhook deleted")
package main
import (
"context"
"fmt"
"os"
"github.com/Access-Grid/accessgrid-go"
)
func main() {
accountID := os.Getenv("ACCOUNT_ID")
secretKey := os.Getenv("SECRET_KEY")
client, err := accessgrid.NewClient(accountID, secretKey)
if err != nil {
fmt.Printf("Error creating client: %v\n", err)
return
}
ctx := context.Background()
err = client.Console.Webhooks.Delete(ctx, "abc123")
if err != nil {
fmt.Printf("Error deleting webhook: %v\n", err)
return
}
fmt.Println("Webhook deleted")
}
using AccessGrid;
using System;
using System.Threading.Tasks;
public async Task DeleteWebhookAsync()
{
var accountId = Environment.GetEnvironmentVariable("ACCOUNT_ID");
var secretKey = Environment.GetEnvironmentVariable("SECRET_KEY");
var client = new AccessGridClient(accountId, secretKey);
await client.Console.Webhooks.DeleteAsync("abc123");
Console.WriteLine("Webhook deleted");
}
import com.organization.accessgrid.AccessGridClient;
public class WebhookService {
private final AccessGridClient client;
public WebhookService() {
String accountId = System.getenv("ACCOUNT_ID");
String secretKey = System.getenv("SECRET_KEY");
this.client = new AccessGridClient(accountId, secretKey);
}
public void deleteWebhook() {
client.console().webhooks().delete("abc123");
System.out.println("Webhook deleted");
}
}
<?php
require 'vendor/autoload.php';
use AccessGridClient;
$accountId = $_ENV['ACCOUNT_ID'];
$secretKey = $_ENV['SECRET_KEY'];
$client = new Client($accountId, $secretKey);
$client->console->deleteWebhook('abc123');
echo "Webhook deleted\n";
client = AccessGrid.Client.new(
account_id: System.get_env("ACCOUNT_ID"),
api_secret: System.get_env("SECRET_KEY")
)
:ok = AccessGrid.Console.delete_webhook("abc123", client: client)
IO.puts("Webhook deleted")
Response
Empty
Verify Webhook
Trigger verification of a webhook. Webhooks must be verified before they receive event deliveries; verification normally happens automatically when a webhook is created or its URL changes, but this endpoint lets you re-run the handshake on demand (for example, after fixing an endpoint that failed the initial challenge).
If the webhook is already verified, the request returns 200 OK and nothing changes. Otherwise AccessGrid (re)initiates the challenge-response handshake — sending a POST to your webhook URL with an X-AccessGrid-Webhook-Challenge header and a { "challenge": "<token>" } body that your endpoint must echo back — and returns 202 Accepted. Poll List Webhooks to observe the resulting verified state.
webhook_id
string
Request
# Build the JSON payload
PAYLOAD="{}"
# Sign the payload
PAYLOAD_B64=$(printf '%s' "$PAYLOAD" | openssl base64 -A)
SIG=$(printf '%s' "$PAYLOAD_B64" | openssl dgst -sha256 -hmac "$SECRET_KEY" -hex | awk '{print $NF}')
curl -X POST \
-H "X-ACCT-ID: $ACCOUNT_ID" \
-H "X-PAYLOAD-SIG: $SIG" \
-H "Content-Type: application/json" \
-d "$PAYLOAD" \
"https://api.accessgrid.com/v1/console/webhooks/{webhook_id}/verify"
require 'accessgrid'
account_id = ENV['ACCOUNT_ID']
secret_key = ENV['SECRET_KEY']
client = AccessGrid::Client.new(account_id, secret_key)
result = client.console.webhooks.verify('abc123')
puts "Verified: #{result.verified}"
import AccessGrid from 'accessgrid';
const accountId = process.env.ACCOUNT_ID;
const secretKey = process.env.SECRET_KEY;
const client = new AccessGrid(accountId, secretKey);
const verifyWebhook = async () => {
try {
const result = await client.console.webhooks.verify('abc123');
console.log(`Verified: ${result.verified}`);
} catch (error) {
console.error('Error verifying webhook:', error);
}
};
verifyWebhook();
from accessgrid import AccessGrid
import os
account_id = os.getenv('ACCOUNT_ID')
secret_key = os.getenv('SECRET_KEY')
client = AccessGrid(account_id, secret_key)
result = client.console.webhooks.verify("abc123")
print(f"Verified: {result.verified}")
package main
import (
"context"
"fmt"
"os"
"github.com/Access-Grid/accessgrid-go"
)
func main() {
accountID := os.Getenv("ACCOUNT_ID")
secretKey := os.Getenv("SECRET_KEY")
client, err := accessgrid.NewClient(accountID, secretKey)
if err != nil {
fmt.Printf("Error creating client: %v\n", err)
return
}
ctx := context.Background()
result, err := client.Console.Webhooks.Verify(ctx, "abc123")
if err != nil {
fmt.Printf("Error verifying webhook: %v\n", err)
return
}
fmt.Printf("Verified: %t\n", result.Verified)
}
using AccessGrid;
using System;
using System.Threading.Tasks;
public async Task VerifyWebhookAsync()
{
var accountId = Environment.GetEnvironmentVariable("ACCOUNT_ID");
var secretKey = Environment.GetEnvironmentVariable("SECRET_KEY");
var client = new AccessGridClient(accountId, secretKey);
var result = await client.Console.Webhooks.VerifyAsync("abc123");
Console.WriteLine($"Verified: {result.Verified}");
}
import com.organization.accessgrid.AccessGridClient;
import com.organization.accessgrid.Models;
public class WebhookService {
private final AccessGridClient client;
public WebhookService() {
String accountId = System.getenv("ACCOUNT_ID");
String secretKey = System.getenv("SECRET_KEY");
this.client = new AccessGridClient(accountId, secretKey);
}
public void verifyWebhook() {
Models.WebhookVerification result = client.console().webhooks().verify("abc123");
System.out.printf("Verified: %b%n", result.isVerified());
}
}
<?php
require 'vendor/autoload.php';
use AccessGridClient;
$accountId = $_ENV['ACCOUNT_ID'];
$secretKey = $_ENV['SECRET_KEY'];
$client = new Client($accountId, $secretKey);
$result = $client->console->verifyWebhook('abc123');
echo "Verified: " . ($result->verified ? 'true' : 'false') . "\n";
client = AccessGrid.Client.new(
account_id: System.get_env("ACCOUNT_ID"),
api_secret: System.get_env("SECRET_KEY")
)
{:ok, result} = AccessGrid.Console.verify_webhook("abc123", client: client)
IO.puts("Verified: #{result.verified}")
Response
// 202 Accepted - verification challenge (re)initiated
{
"id": "abc123",
"verified": false
}
// 200 OK - webhook already verified, no action taken
{
"id": "abc123",
"verified": true
}