Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
## [1.0.0] - 2026-03-11

### Added
- `toggle_webhook` method to enable or disable a webhook without deleting it (PATCH `/v0/webhooks/{webhookID}`)
- `ToggleWebhookRequest` type for the toggle webhook endpoint
- `active` field on the `Webhook` struct indicating whether the webhook is actively receiving deliveries
- `HeliusBuilder` for flexible client configuration (custom timeouts, TLS, connection settings)
- ZK Compression support: 20+ new RPC methods for compressed accounts, token accounts, balances, proofs, and signatures
- Wallet API support: identity, balances, transfers, transaction history, and funding source endpoints
Expand Down
7 changes: 7 additions & 0 deletions llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,13 @@ let webhook = helius.create_webhook(CreateWebhookRequest {
// List all webhooks
let webhooks = helius.get_all_webhooks().await?;

// Toggle a webhook on or off (re-enable after auto-disable, or pause deliveries)
use helius::types::ToggleWebhookRequest;
let updated = helius.toggle_webhook(ToggleWebhookRequest {
webhook_id: "webhook_id".to_string(),
active: true, // or false to disable
}).await?;

// Delete a webhook
helius.delete_webhook("webhook_id").await?;
```
Expand Down
23 changes: 23 additions & 0 deletions src/types/inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1600,6 +1600,14 @@ pub struct Webhook {
/// The encoding for raw webhook payloads (default: `JsonParsed`)
#[serde(default)]
pub encoding: AccountWebhookEncoding,
/// Whether the webhook is actively receiving deliveries. Defaults to `true`.
/// Webhooks may be automatically disabled if the endpoint has a high failure rate.
#[serde(default = "default_active")]
pub active: bool,
}

fn default_active() -> bool {
true
}

/// Request body for creating a new Helius webhook.
Expand Down Expand Up @@ -1692,6 +1700,21 @@ pub struct EditWebhookRequest {
pub encoding: AccountWebhookEncoding,
}

/// Request body for toggling a webhook on or off.
///
/// Enables or disables a webhook without deleting it. Use this to re-enable a webhook
/// that was automatically disabled due to a high endpoint failure rate, or to temporarily
/// pause deliveries. After re-enabling, the webhook enters a 24-hour grace period during
/// which it will not be automatically disabled again.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ToggleWebhookRequest {
/// The ID of the webhook to toggle (not serialized — used in the URL path)
#[serde(skip_serializing)]
pub webhook_id: String,
/// Set to `true` to enable the webhook or `false` to disable it
pub active: bool,
}

/// Configuration for creating and sending a smart transaction via Helius.
///
/// Smart transactions automatically handle priority fee estimation, compute unit
Expand Down
29 changes: 28 additions & 1 deletion src/webhook.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::error::Result;
use crate::types::{CreateWebhookRequest, EditWebhookRequest, Webhook};
use crate::types::{CreateWebhookRequest, EditWebhookRequest, ToggleWebhookRequest, Webhook};
use crate::Helius;

use reqwest::{Method, Url};
Expand Down Expand Up @@ -137,6 +137,33 @@ impl Helius {
self.rpc_client.handler.send(Method::GET, parsed_url, None::<&()>).await
}

/// Toggles a webhook on or off without deleting it
///
/// Use this to re-enable a webhook that was automatically disabled due to a high endpoint
/// failure rate, or to temporarily pause deliveries. After re-enabling, the webhook enters
/// a 24-hour grace period during which it will not be automatically disabled again.
///
/// # Arguments
/// * `request` - A `ToggleWebhookRequest` containing the webhook ID and the desired `active` state
///
/// # Returns
/// A `Result` wrapping the updated `Webhook`, or a `HeliusError` if the request fails
pub async fn toggle_webhook(&self, request: ToggleWebhookRequest) -> Result<Webhook> {
let api_key = self.config.require_api_key("webhook operations")?;
let url: String = format!(
"{}v0/webhooks/{}?api-key={}",
self.config.endpoints.api,
request.webhook_id,
api_key.as_str()
);
let parsed_url: Url = Url::parse(&url).expect("Failed to parse URL");

self.rpc_client
.handler
.send(Method::PATCH, parsed_url, Some(&request))
.await
}

/// Deletes a given Helius webhook programmatically
///
/// # Arguments
Expand Down
1 change: 1 addition & 0 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ mod webhook {
mod test_delete_webhook;
mod test_get_all_webhooks;
mod test_remove_addresses_from_webhook;
mod test_toggle_webhook;
}
mod zk_compression {
mod helpers;
Expand Down
106 changes: 106 additions & 0 deletions tests/webhook/test_toggle_webhook.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use helius::config::Config;
use helius::error::Result;
use helius::rpc_client::RpcClient;
use helius::types::{ApiKey, Cluster, HeliusEndpoints, ToggleWebhookRequest, TransactionType, Webhook, WebhookType};
use helius::Helius;
use mockito::Server;
use reqwest::Client;
use std::sync::Arc;

#[tokio::test]
async fn test_toggle_webhook_success() {
let mut server: Server = Server::new_with_opts_async(mockito::ServerOpts::default()).await;
let url: String = format!("{}/", server.url());

let mock_response: Webhook = Webhook {
webhook_url: "https://webhook.site/0e8250a1-ceec-4757-ad69-cc6473085bfc".to_string(),
transaction_types: vec![TransactionType::Any],
account_addresses: vec![],
webhook_type: WebhookType::Enhanced,
auth_header: None,
webhook_id: "0e8250a1-ceec-4757-ad69".to_string(),
wallet: "9Jt8mC9HXvh2g5s3PbTsNU71RS9MXUbhEMEmLTixYirb".to_string(),
project: "Mockito".to_string(),
active: false,
..Default::default()
};

server
.mock("PATCH", "/v0/webhooks/0e8250a1-ceec-4757-ad69?api-key=fake_api_key")
.with_status(200)
.with_header("Content-Type", "application/json")
.with_body(serde_json::to_string(&mock_response).unwrap())
.create();

let config: Arc<Config> = Arc::new(Config {
api_key: Some(ApiKey::new("fake_api_key").unwrap()),
cluster: Cluster::Devnet,
endpoints: HeliusEndpoints {
api: url.to_string(),
rpc: url.to_string(),
},
custom_url: None,
});

let client: Client = Client::new();
let rpc_client: Arc<RpcClient> = Arc::new(RpcClient::new(Arc::new(client.clone()), Arc::clone(&config)).unwrap());
let helius: Helius = Helius {
config,
client,
rpc_client,
async_rpc_client: None,
ws_client: None,
};

let request: ToggleWebhookRequest = ToggleWebhookRequest {
webhook_id: "0e8250a1-ceec-4757-ad69".to_string(),
active: false,
};
let response: Result<Webhook> = helius.toggle_webhook(request).await;

assert!(response.is_ok(), "The API call failed: {:?}", response.err());
let webhook_response: Webhook = response.unwrap();

assert_eq!(webhook_response.webhook_id, "0e8250a1-ceec-4757-ad69");
assert!(!webhook_response.active);
}

#[tokio::test]
async fn test_toggle_webhook_failure() {
let mut server: Server = Server::new_with_opts_async(mockito::ServerOpts::default()).await;
let url: String = format!("{}/", server.url());

server
.mock("PATCH", "/v0/webhooks/0e8250a1-ceec-4757-ad69?api-key=fake_api_key")
.with_status(500)
.with_header("Content-Type", "application/json")
.with_body(r#"{"error":"Internal Server Error"}"#)
.create();

let config: Arc<Config> = Arc::new(Config {
api_key: Some(ApiKey::new("fake_api_key").unwrap()),
cluster: Cluster::Devnet,
endpoints: HeliusEndpoints {
api: url.to_string(),
rpc: url.to_string(),
},
custom_url: None,
});
let client: Client = Client::new();
let rpc_client: Arc<RpcClient> = Arc::new(RpcClient::new(Arc::new(client.clone()), Arc::clone(&config)).unwrap());
let helius: Helius = Helius {
config,
client,
rpc_client,
async_rpc_client: None,
ws_client: None,
};

let request: ToggleWebhookRequest = ToggleWebhookRequest {
webhook_id: "0e8250a1-ceec-4757-ad69".to_string(),
active: true,
};

let response: Result<Webhook> = helius.toggle_webhook(request).await;
assert!(response.is_err(), "Expected an error due to server failure");
}
Loading