From 3792998668a78aba3be91f0f44abb75059d2065c Mon Sep 17 00:00:00 2001 From: Auston Bunsen Date: Mon, 30 Mar 2026 20:44:23 -0400 Subject: [PATCH 1/2] Add HID orgs support (create, list, activate) Implements Console.HID.Orgs.Create(), Console.HID.Orgs.List(), and Console.HID.Orgs.Activate() to match the AccessGrid API docs. Adds feature matrix to README. --- README.md | 60 +++++++++++++++++++- accessgrid.go | 9 +++ models/models.go | 28 ++++++++++ services/console.go | 60 +++++++++++++++++++- services/console_test.go | 118 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 270 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b661b49..8c9479b 100644 --- a/README.md +++ b/README.md @@ -433,6 +433,60 @@ func main() { } ``` +### HID Organizations + +#### Create an HID org + +```go +ctx := context.Background() +org, err := client.Console.HID.Orgs.Create(ctx, &accessgrid.CreateHIDOrgParams{ + Name: "My Org", + FullAddress: "1 Main St, NY NY", + Phone: "+1-555-0000", + FirstName: "Ada", + LastName: "Lovelace", +}) +if err != nil { + fmt.Printf("Error creating org: %v\n", err) + return +} + +fmt.Printf("Created org: %s (ID: %s)\n", org.Name, org.ID) +fmt.Printf("Slug: %s\n", org.Slug) +``` + +#### List HID orgs + +```go +ctx := context.Background() +orgs, err := client.Console.HID.Orgs.List(ctx) +if err != nil { + fmt.Printf("Error listing orgs: %v\n", err) + return +} + +for _, org := range orgs { + fmt.Printf("Org ID: %s, Name: %s, Slug: %s\n", org.ID, org.Name, org.Slug) +} +``` + +#### Activate an HID org + +```go +ctx := context.Background() +result, err := client.Console.HID.Orgs.Activate(ctx, &accessgrid.CompleteHIDOrgParams{ + Email: "admin@example.com", + Password: "hid-password-123", +}) +if err != nil { + fmt.Printf("Error completing registration: %v\n", err) + return +} + +fmt.Printf("Completed registration for org: %s\n", result.Name) +fmt.Printf("Status: %s\n", result.Status) +``` + ## Configuration The SDK can be configured with custom options: @@ -503,9 +557,9 @@ Never expose your `secretKey` in source code. Always use environment variables o | GET /v1/console/webhooks | `Console.Webhooks.List()` | - | | POST /v1/console/webhooks | `Console.Webhooks.Create()` | - | | DELETE /v1/console/webhooks/{id} | `Console.Webhooks.Delete()` | - | -| POST /v1/console/hid/orgs | `Console.HID.Orgs.Create()` | - | -| POST /v1/console/hid/orgs/activate | `Console.HID.Orgs.Activate()` | - | -| GET /v1/console/hid/orgs | `Console.HID.Orgs.List()` | - | +| POST /v1/console/hid/orgs | `Console.HID.Orgs.Create()` | Y | +| POST /v1/console/hid/orgs/activate | `Console.HID.Orgs.Activate()` | Y | +| GET /v1/console/hid/orgs | `Console.HID.Orgs.List()` | Y | ## License diff --git a/accessgrid.go b/accessgrid.go index cc8a71f..e23aa3a 100644 --- a/accessgrid.go +++ b/accessgrid.go @@ -110,4 +110,13 @@ type ( // ListLedgerItemsParams defines parameters for listing ledger items ListLedgerItemsParams = models.ListLedgerItemsParams + + // HIDOrg represents an HID organization + HIDOrg = models.HIDOrg + + // CreateHIDOrgParams defines parameters for creating an HID organization + CreateHIDOrgParams = models.CreateHIDOrgParams + + // CompleteHIDOrgParams defines parameters for completing HID org registration + CompleteHIDOrgParams = models.CompleteHIDOrgParams ) diff --git a/models/models.go b/models/models.go index 49eb007..daef9d8 100644 --- a/models/models.go +++ b/models/models.go @@ -273,3 +273,31 @@ type ListLedgerItemsParams struct { StartDate *time.Time `json:"start_date,omitempty"` EndDate *time.Time `json:"end_date,omitempty"` } + +// HIDOrg represents an HID organization +type HIDOrg struct { + ID string `json:"id"` + Name string `json:"name"` + Slug string `json:"slug"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + Phone string `json:"phone"` + FullAddress string `json:"full_address"` + Status string `json:"status"` + CreatedAt string `json:"created_at"` +} + +// CreateHIDOrgParams defines parameters for creating an HID organization +type CreateHIDOrgParams struct { + Name string `json:"name"` + FullAddress string `json:"full_address"` + Phone string `json:"phone"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` +} + +// CompleteHIDOrgParams defines parameters for completing HID org registration +type CompleteHIDOrgParams struct { + Email string `json:"email"` + Password string `json:"password"` +} diff --git a/services/console.go b/services/console.go index 387610c..fe66541 100644 --- a/services/console.go +++ b/services/console.go @@ -14,11 +14,67 @@ import ( // ConsoleService handles operations related to the enterprise console type ConsoleService struct { client *client.Client + HID *HIDService } // NewConsoleService creates a new ConsoleService -func NewConsoleService(client *client.Client) *ConsoleService { - return &ConsoleService{client: client} +func NewConsoleService(c *client.Client) *ConsoleService { + return &ConsoleService{ + client: c, + HID: NewHIDService(c), + } +} + +// HIDService provides access to HID-related services +type HIDService struct { + Orgs *HIDOrgsService +} + +// NewHIDService creates a new HIDService +func NewHIDService(c *client.Client) *HIDService { + return &HIDService{ + Orgs: NewHIDOrgsService(c), + } +} + +// HIDOrgsService handles HID organization operations +type HIDOrgsService struct { + client *client.Client +} + +// NewHIDOrgsService creates a new HIDOrgsService +func NewHIDOrgsService(c *client.Client) *HIDOrgsService { + return &HIDOrgsService{client: c} +} + +// Create creates a new HID organization +func (s *HIDOrgsService) Create(ctx context.Context, params *models.CreateHIDOrgParams) (*models.HIDOrg, error) { + var org models.HIDOrg + err := s.client.Request(ctx, http.MethodPost, "/v1/console/hid/orgs", params, &org) + if err != nil { + return nil, fmt.Errorf("error creating HID org: %w", err) + } + return &org, nil +} + +// List retrieves all HID organizations +func (s *HIDOrgsService) List(ctx context.Context) ([]models.HIDOrg, error) { + var orgs []models.HIDOrg + err := s.client.Request(ctx, http.MethodGet, "/v1/console/hid/orgs", nil, &orgs) + if err != nil { + return nil, fmt.Errorf("error listing HID orgs: %w", err) + } + return orgs, nil +} + +// Activate completes HID org registration with credentials +func (s *HIDOrgsService) Activate(ctx context.Context, params *models.CompleteHIDOrgParams) (*models.HIDOrg, error) { + var org models.HIDOrg + err := s.client.Request(ctx, http.MethodPost, "/v1/console/hid/orgs/activate", params, &org) + if err != nil { + return nil, fmt.Errorf("error activating HID org: %w", err) + } + return &org, nil } // CreateTemplate creates a new card template diff --git a/services/console_test.go b/services/console_test.go index e347ea6..e31a5e0 100644 --- a/services/console_test.go +++ b/services/console_test.go @@ -612,6 +612,124 @@ func TestConsoleService_ListLedgerItems_WithFilters(t *testing.T) { } } +// --- HID Orgs --- + +func TestHIDOrgsService_Create(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/v1/console/hid/orgs" || r.Method != http.MethodPost { + t.Errorf("unexpected request: %s %s", r.Method, r.URL.Path) + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + w.Write([]byte(`{ + "id": "org_123", + "name": "My Org", + "slug": "my-org", + "first_name": "Ada", + "last_name": "Lovelace", + "phone": "+1-555-0000", + "full_address": "1 Main St, NY NY", + "status": "pending", + "created_at": "2025-01-01T00:00:00Z" + }`)) + })) + defer server.Close() + + c, _ := client.NewClient("test-account", "test-secret", client.WithBaseURL(server.URL)) + service := NewHIDOrgsService(c) + + ctx := context.Background() + org, err := service.Create(ctx, &models.CreateHIDOrgParams{ + Name: "My Org", + FullAddress: "1 Main St, NY NY", + Phone: "+1-555-0000", + FirstName: "Ada", + LastName: "Lovelace", + }) + if err != nil { + t.Fatalf("Create() error = %v", err) + } + + if org.ID != "org_123" { + t.Errorf("org.ID = %v, want org_123", org.ID) + } + if org.Name != "My Org" { + t.Errorf("org.Name = %v, want My Org", org.Name) + } + if org.Slug != "my-org" { + t.Errorf("org.Slug = %v, want my-org", org.Slug) + } + if org.Status != "pending" { + t.Errorf("org.Status = %v, want pending", org.Status) + } +} + +func TestHIDOrgsService_List(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`[ + {"id": "org_1", "name": "Org One", "slug": "org-one", "status": "active", "created_at": "2025-01-01T00:00:00Z"}, + {"id": "org_2", "name": "Org Two", "slug": "org-two", "status": "pending", "created_at": "2025-01-02T00:00:00Z"} + ]`)) + })) + defer server.Close() + + c, _ := client.NewClient("test-account", "test-secret", client.WithBaseURL(server.URL)) + service := NewHIDOrgsService(c) + + ctx := context.Background() + orgs, err := service.List(ctx) + if err != nil { + t.Fatalf("List() error = %v", err) + } + + if len(orgs) != 2 { + t.Fatalf("got %d orgs, want 2", len(orgs)) + } + if orgs[0].ID != "org_1" { + t.Errorf("orgs[0].ID = %v, want org_1", orgs[0].ID) + } + if orgs[1].Name != "Org Two" { + t.Errorf("orgs[1].Name = %v, want Org Two", orgs[1].Name) + } +} + +func TestHIDOrgsService_Activate(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/v1/console/hid/orgs/activate" || r.Method != http.MethodPost { + t.Errorf("unexpected request: %s %s", r.Method, r.URL.Path) + } + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{ + "id": "org_123", + "name": "My Org", + "slug": "my-org", + "status": "active", + "created_at": "2025-01-01T00:00:00Z" + }`)) + })) + defer server.Close() + + c, _ := client.NewClient("test-account", "test-secret", client.WithBaseURL(server.URL)) + service := NewHIDOrgsService(c) + + ctx := context.Background() + org, err := service.Activate(ctx, &models.CompleteHIDOrgParams{ + Email: "admin@example.com", + Password: "hid-password-123", + }) + if err != nil { + t.Fatalf("Activate() error = %v", err) + } + + if org.Status != "active" { + t.Errorf("org.Status = %v, want active", org.Status) + } + if org.Name != "My Org" { + t.Errorf("org.Name = %v, want My Org", org.Name) + } +} + func TestConsoleService_ErrorPropagation(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") From a6b2556a757522f134902d07339bed2e82167a66 Mon Sep 17 00:00:00 2001 From: Auston Bunsen Date: Mon, 30 Mar 2026 21:17:28 -0400 Subject: [PATCH 2/2] Add iOS preflight and webhooks support Implements Console.IosPreflight(), Console.Webhooks.Create(), Console.Webhooks.List(), and Console.Webhooks.Delete(). Updates feature matrix to show all features as supported. --- README.md | 8 +-- accessgrid.go | 15 ++++ models/models.go | 41 +++++++++++ services/console.go | 65 ++++++++++++++++-- services/console_test.go | 143 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 264 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 8c9479b..577d31f 100644 --- a/README.md +++ b/README.md @@ -552,11 +552,11 @@ Never expose your `secretKey` in source code. Always use environment variables o | GET /v1/console/card-templates/{id} | `Console.ReadTemplate()` | Y | | GET /v1/console/card-templates/{id}/logs | `Console.EventLog()` | Y | | GET /v1/console/pass-template-pairs | `Console.ListPassTemplatePairs()` | Y | -| POST /v1/console/card-templates/{id}/ios_preflight | `Console.IosPreflight()` | - | +| POST /v1/console/card-templates/{id}/ios_preflight | `Console.IosPreflight()` | Y | | GET /v1/console/ledger-items | `Console.ListLedgerItems()` | Y | -| GET /v1/console/webhooks | `Console.Webhooks.List()` | - | -| POST /v1/console/webhooks | `Console.Webhooks.Create()` | - | -| DELETE /v1/console/webhooks/{id} | `Console.Webhooks.Delete()` | - | +| GET /v1/console/webhooks | `Console.Webhooks.List()` | Y | +| POST /v1/console/webhooks | `Console.Webhooks.Create()` | Y | +| DELETE /v1/console/webhooks/{id} | `Console.Webhooks.Delete()` | Y | | POST /v1/console/hid/orgs | `Console.HID.Orgs.Create()` | Y | | POST /v1/console/hid/orgs/activate | `Console.HID.Orgs.Activate()` | Y | | GET /v1/console/hid/orgs | `Console.HID.Orgs.List()` | Y | diff --git a/accessgrid.go b/accessgrid.go index e23aa3a..9d0cc3b 100644 --- a/accessgrid.go +++ b/accessgrid.go @@ -111,6 +111,21 @@ type ( // ListLedgerItemsParams defines parameters for listing ledger items ListLedgerItemsParams = models.ListLedgerItemsParams + // IosPreflight represents an iOS In-App Provisioning preflight response + IosPreflight = models.IosPreflight + + // IosPreflightParams defines parameters for iOS preflight + IosPreflightParams = models.IosPreflightParams + + // Webhook represents a webhook configuration + Webhook = models.Webhook + + // WebhooksResponse represents the response from listing webhooks + WebhooksResponse = models.WebhooksResponse + + // CreateWebhookParams defines parameters for creating a webhook + CreateWebhookParams = models.CreateWebhookParams + // HIDOrg represents an HID organization HIDOrg = models.HIDOrg diff --git a/models/models.go b/models/models.go index daef9d8..4171ff7 100644 --- a/models/models.go +++ b/models/models.go @@ -274,6 +274,47 @@ type ListLedgerItemsParams struct { EndDate *time.Time `json:"end_date,omitempty"` } +// IosPreflight represents an iOS In-App Provisioning preflight response +type IosPreflight struct { + ProvisioningCredentialIdentifier string `json:"provisioningCredentialIdentifier"` + SharingInstanceIdentifier string `json:"sharingInstanceIdentifier"` + CardTemplateIdentifier string `json:"cardTemplateIdentifier"` + EnvironmentIdentifier string `json:"environmentIdentifier"` +} + +// IosPreflightParams defines parameters for iOS preflight +type IosPreflightParams struct { + CardTemplateID string `json:"card_template_id"` + AccessPassExID string `json:"access_pass_ex_id"` +} + +// Webhook represents a webhook configuration +type Webhook struct { + ID string `json:"id"` + Name string `json:"name"` + URL string `json:"url"` + AuthMethod string `json:"auth_method"` + SubscribedEvents []string `json:"subscribed_events"` + CreatedAt string `json:"created_at"` + PrivateKey string `json:"private_key,omitempty"` + ClientCert string `json:"client_cert,omitempty"` + CertExpiresAt string `json:"cert_expires_at,omitempty"` +} + +// WebhooksResponse represents the response from listing webhooks +type WebhooksResponse struct { + Webhooks []Webhook `json:"webhooks"` + Pagination Pagination `json:"pagination"` +} + +// CreateWebhookParams defines parameters for creating a webhook +type CreateWebhookParams struct { + Name string `json:"name"` + URL string `json:"url"` + SubscribedEvents []string `json:"subscribed_events"` + AuthMethod string `json:"auth_method,omitempty"` +} + // HIDOrg represents an HID organization type HIDOrg struct { ID string `json:"id"` diff --git a/services/console.go b/services/console.go index fe66541..4b5265d 100644 --- a/services/console.go +++ b/services/console.go @@ -13,16 +13,73 @@ import ( // ConsoleService handles operations related to the enterprise console type ConsoleService struct { - client *client.Client - HID *HIDService + client *client.Client + HID *HIDService + Webhooks *WebhooksService } // NewConsoleService creates a new ConsoleService func NewConsoleService(c *client.Client) *ConsoleService { return &ConsoleService{ - client: c, - HID: NewHIDService(c), + client: c, + HID: NewHIDService(c), + Webhooks: NewWebhooksService(c), + } +} + +// IosPreflight retrieves iOS In-App Provisioning identifiers +func (s *ConsoleService) IosPreflight(ctx context.Context, params models.IosPreflightParams) (*models.IosPreflight, error) { + var result models.IosPreflight + path := fmt.Sprintf("/v1/console/card-templates/%s/ios_preflight", url.PathEscape(params.CardTemplateID)) + body := map[string]string{"access_pass_ex_id": params.AccessPassExID} + err := s.client.Request(ctx, http.MethodPost, path, body, &result) + if err != nil { + return nil, fmt.Errorf("error fetching iOS preflight: %w", err) + } + return &result, nil +} + +// WebhooksService handles webhook operations +type WebhooksService struct { + client *client.Client +} + +// NewWebhooksService creates a new WebhooksService +func NewWebhooksService(c *client.Client) *WebhooksService { + return &WebhooksService{client: c} +} + +// Create creates a new webhook +func (s *WebhooksService) Create(ctx context.Context, params models.CreateWebhookParams) (*models.Webhook, error) { + if params.AuthMethod == "" { + params.AuthMethod = "bearer_token" } + var webhook models.Webhook + err := s.client.Request(ctx, http.MethodPost, "/v1/console/webhooks", params, &webhook) + if err != nil { + return nil, fmt.Errorf("error creating webhook: %w", err) + } + return &webhook, nil +} + +// List retrieves all webhooks +func (s *WebhooksService) List(ctx context.Context) (*models.WebhooksResponse, error) { + var response models.WebhooksResponse + err := s.client.Request(ctx, http.MethodGet, "/v1/console/webhooks", nil, &response) + if err != nil { + return nil, fmt.Errorf("error listing webhooks: %w", err) + } + return &response, nil +} + +// Delete deletes a webhook by ID +func (s *WebhooksService) Delete(ctx context.Context, webhookID string) error { + path := fmt.Sprintf("/v1/console/webhooks/%s", url.PathEscape(webhookID)) + err := s.client.Request(ctx, http.MethodDelete, path, nil, nil) + if err != nil { + return fmt.Errorf("error deleting webhook: %w", err) + } + return nil } // HIDService provides access to HID-related services diff --git a/services/console_test.go b/services/console_test.go index e31a5e0..1cdd5ec 100644 --- a/services/console_test.go +++ b/services/console_test.go @@ -612,6 +612,149 @@ func TestConsoleService_ListLedgerItems_WithFilters(t *testing.T) { } } +// --- iOS Preflight --- + +func TestConsoleService_IosPreflight(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + t.Errorf("expected POST, got %s", r.Method) + } + if !strings.Contains(r.URL.Path, "/ios_preflight") { + t.Errorf("expected ios_preflight path, got %s", r.URL.Path) + } + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{ + "provisioningCredentialIdentifier": "prov_cred_123", + "sharingInstanceIdentifier": "share_inst_456", + "cardTemplateIdentifier": "card_tmpl_789", + "environmentIdentifier": "env_abc" + }`)) + })) + defer server.Close() + + c, _ := client.NewClient("test-account", "test-secret", client.WithBaseURL(server.URL)) + service := NewConsoleService(c) + + ctx := context.Background() + result, err := service.IosPreflight(ctx, models.IosPreflightParams{ + CardTemplateID: "tmpl_123", + AccessPassExID: "pass_456", + }) + if err != nil { + t.Fatalf("IosPreflight() error = %v", err) + } + + if result.ProvisioningCredentialIdentifier != "prov_cred_123" { + t.Errorf("ProvisioningCredentialIdentifier = %v, want prov_cred_123", result.ProvisioningCredentialIdentifier) + } + if result.SharingInstanceIdentifier != "share_inst_456" { + t.Errorf("SharingInstanceIdentifier = %v, want share_inst_456", result.SharingInstanceIdentifier) + } + if result.CardTemplateIdentifier != "card_tmpl_789" { + t.Errorf("CardTemplateIdentifier = %v, want card_tmpl_789", result.CardTemplateIdentifier) + } + if result.EnvironmentIdentifier != "env_abc" { + t.Errorf("EnvironmentIdentifier = %v, want env_abc", result.EnvironmentIdentifier) + } +} + +// --- Webhooks --- + +func TestWebhooksService_Create(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/v1/console/webhooks" || r.Method != http.MethodPost { + t.Errorf("unexpected request: %s %s", r.Method, r.URL.Path) + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + w.Write([]byte(`{ + "id": "wh_123", + "name": "Production", + "url": "https://example.com/webhooks", + "auth_method": "bearer_token", + "subscribed_events": ["ag.access_pass.issued"], + "created_at": "2025-01-01T00:00:00Z", + "private_key": "pk_secret_123" + }`)) + })) + defer server.Close() + + c, _ := client.NewClient("test-account", "test-secret", client.WithBaseURL(server.URL)) + service := NewWebhooksService(c) + + ctx := context.Background() + webhook, err := service.Create(ctx, models.CreateWebhookParams{ + Name: "Production", + URL: "https://example.com/webhooks", + SubscribedEvents: []string{"ag.access_pass.issued"}, + }) + if err != nil { + t.Fatalf("Create() error = %v", err) + } + + if webhook.ID != "wh_123" { + t.Errorf("webhook.ID = %v, want wh_123", webhook.ID) + } + if webhook.Name != "Production" { + t.Errorf("webhook.Name = %v, want Production", webhook.Name) + } + if webhook.PrivateKey != "pk_secret_123" { + t.Errorf("webhook.PrivateKey = %v, want pk_secret_123", webhook.PrivateKey) + } +} + +func TestWebhooksService_List(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{ + "webhooks": [ + {"id": "wh_1", "name": "Prod", "url": "https://example.com/wh1", "auth_method": "bearer_token"}, + {"id": "wh_2", "name": "Staging", "url": "https://example.com/wh2", "auth_method": "bearer_token"} + ], + "pagination": {"current_page": 1, "total_pages": 1} + }`)) + })) + defer server.Close() + + c, _ := client.NewClient("test-account", "test-secret", client.WithBaseURL(server.URL)) + service := NewWebhooksService(c) + + ctx := context.Background() + response, err := service.List(ctx) + if err != nil { + t.Fatalf("List() error = %v", err) + } + + if len(response.Webhooks) != 2 { + t.Fatalf("got %d webhooks, want 2", len(response.Webhooks)) + } + if response.Webhooks[0].ID != "wh_1" { + t.Errorf("webhooks[0].ID = %v, want wh_1", response.Webhooks[0].ID) + } +} + +func TestWebhooksService_Delete(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodDelete { + t.Errorf("expected DELETE, got %s", r.Method) + } + if !strings.Contains(r.URL.Path, "/v1/console/webhooks/wh_123") { + t.Errorf("expected webhook path, got %s", r.URL.Path) + } + w.WriteHeader(http.StatusNoContent) + })) + defer server.Close() + + c, _ := client.NewClient("test-account", "test-secret", client.WithBaseURL(server.URL)) + service := NewWebhooksService(c) + + ctx := context.Background() + err := service.Delete(ctx, "wh_123") + if err != nil { + t.Errorf("Delete() error = %v", err) + } +} + // --- HID Orgs --- func TestHIDOrgsService_Create(t *testing.T) {