This endpoint subscribes to new transactions associated with the wallets.
NOTE: Consider all IDs as abstract strings, without making any assumptions about their format or relying on such assumptions. There is a non-zero probability that IDs may change in the future, and this should not result in any breaking changes.
The main use case for the webhooks system is to send notifications, similar to push notifications in the Zerion App.
If you want to use this endpoint to test how it works, you might use your dev key to start. It has limits: one subscription & maximum 5 wallets per subscription. You may use callback URL from webhook.site to start from. If want use your custom - contact us at api@zerion.io, and we will whitelist your URL.
If you want to use this endpoint in your production environment, you should contact us and provide the following details:
After we’ve whitelisted your callback URL (or host), you may start using this endpoint.
dev keys.null. Prices are added to transactions in the backend after some time. To get prices, query the transactions endpoint by hash.Approved clients will receive notifications via POST requests to their provided URL. These notifications will include a signed notification object in the body (specified below in the Callback section) and a signature in the headers.
Clients should verify the signature provided in the headers of the webhook request to ensure the authenticity of the data. The following headers will be included:
X-Signature: The signature of the request.X-Timestamp: The timestamp of the request.X-Certificate-URL: The URL to download the public certificate used to verify the signature.To verify the signature:
X-Timestamp header value, the request body, and a newline character: $timestamp + "\n" + $request.body + "\n"X-Certificate-URL header to verify the signature in the X-Signature header.Example code in go for message verification:
package signature
import (
"crypto"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"github.com/stretchr/testify/assert"
"testing"
)
// FetchCertificate fetches the certificate from the given URL
func FetchCertificate() (*x509.Certificate, error) {
certBytes := certificate()
block, _ := pem.Decode(certBytes)
if block == nil || block.Type != "CERTIFICATE" {
return nil, errors.New("failed to decode PEM block containing the certificate")
}
return x509.ParseCertificate(block.Bytes)
}
func certificate() []byte {
return []byte(`-----BEGIN CERTIFICATE-----
MIIDMTCCAhmgAwIBAgIUDd3dFMswamyJ5A1bqF0nzS8v2wgwDQYJKoZIhvcNAQEL
BQAwQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC1plcmlvbiBJbmMuMRwwGgYJKoZI
hvcNAQkBFg1hcGlAemVyaW9uLmlvMB4XDTI0MDYyNzE1MzUzM1oXDTI1MDYyNzE1
MzUzM1owQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC1plcmlvbiBJbmMuMRwwGgYJ
KoZIhvcNAQkBFg1hcGlAemVyaW9uLmlvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAwcPVCPLDhS9dLA8s5J6GJ3t0+jWuUCFwI+q6c03xZnhCaz45FepN
MTiApbvPw1Zm8F8JQB4BRp/F5anokNcDSl/qmNtj3M/z/FrsVvGnSH2FOkZu9TLU
LTW5i8Q0LAYrpgiBHrTa2qrRXd2DiMrEs3QZVoylFYc9QIGet3SULPrlSsYEKxfB
iBZDoFw619NnV6/kBO8FS34Lc+WH5SNNHNnItRrxMv4DMAFyFajSn1IwV6LSWSNK
aPJHCzP/Omu95550HQKcXaJYNE/d99NrcLaFI2fCuEVd00nApFo5knKs0FiXpGca
l3cLOQG5SCOzUOjQb6X5CynEV+0QiyYxDwIDAQABoyEwHzAdBgNVHQ4EFgQUVL4m
u0PcI4nJGUS8syLi5DNL44YwDQYJKoZIhvcNAQELBQADggEBAKaA1oqW0D6KxvIp
IZxWf02XK/YFYwxKV55Vas0VWlzNemE2IjlIj0tknZt0EiM9um2FC27U9n3u0ApS
UDrk96dQ+/RY3T3fiuXysa3ZL05OpreRk0aPuFU9rB4iLTgFiv1G/X5XXJ8O7OQb
48u0vQnYXjT/nt72TMUoakjZ68QsP64FkG8mcK62Tg+FVWB9YWTFc0wOjsOt9RzJ
muKCQ7qx7L1GhkxKX4ZhrYItsH1DzXjeP5aniZgLBSPbxt01tUrSjOGN5CLOpdG8
iOnAFP+Nz8S0h2C7hppOHgC+uxY285UrzAZQoMbCREMV+0Mq/aqdF1B6qoKGNGqL
kFbUhvo=
-----END CERTIFICATE-----
`)
}
func VerifySignature(cert *x509.Certificate, message, signature string) error {
pubKey := cert.PublicKey.(*rsa.PublicKey)
hashed := sha256.Sum256([]byte(message))
sigBytes, err := base64.StdEncoding.DecodeString(signature)
if err != nil {
return err
}
return rsa.VerifyPKCS1v15(pubKey, crypto.SHA256, hashed[:], sigBytes)
}
func TestVerifySignature(t *testing.T) {
// Example usage
x509Cert, err := FetchCertificate()
assert.NoError(t, err)
xTimestamp := "2024-07-31T00:17:36Z"
xSignature := "t65gdR8z3NGh/OQRPzGMFmw36JhDNvOe6LxL6K2hCd3SdYQoTGr76dAy1CpsX2G8XVOIYUIctUQvgICQvtDctVjkRZmXuQDvXHOmiJE0ZknORgjVLFoo5JRYKvwt3EPp6SMN7RtedIX17rH1s2Vp3GRQWSjzN7C/cNgInhCQOP0UDjYlaeNT/yW4B2Qt4uY01yK0YhvQJaFHN+NNr7DZAt4FJuDppItqjaYbHTaFNqLlpI1IX7YvQWVhEYTJY6M4T9IdcGYPJKDljckjvmj9mDHZeh/Y6w8eXjLziMSFvlhJeSn1kBIR3nS7lTcwFNv1CPxD3MM7VB++te3mBbFubg=="
messageBody := `{"data":{"attributes":{"timestamp":"2024-07-31T00:17:36.661896043Z"},"id":"15daee90-5028-4b4c-bd49-b4d43fa1a89e","type":"callback"},"included":[{"attributes":{"application_metadata":{"contract_address":"0x8286d601a0ed6cf75e067e0614f73a5b9f024151","method":{"id":"0x7859bb8d","name":""}},"approvals":[],"fee":{"fungible_info":{"flags":{"verified":true},"icon":{"url":"https://cdn.zerion.io/eth.png"},"implementations":[{"address":"","chain_id":"redstone","decimals":18},{"address":"","chain_id":"polygon-zkevm","decimals":18},{"address":"","chain_id":"optimism","decimals":18},{"address":"","chain_id":"zksync-era","decimals":18},{"address":"","chain_id":"mode","decimals":18},{"address":"","chain_id":"base","decimals":18},{"address":"","chain_id":"ethereum","decimals":18},{"address":"","chain_id":"aurora","decimals":18},{"address":"","chain_id":"scroll","decimals":18},{"address":"","chain_id":"rari","decimals":18},{"address":"","chain_id":"astar-zkevm","decimals":18},{"address":"","chain_id":"arbitrum","decimals":18},{"address":"","chain_id":"zora","decimals":18},{"address":"","chain_id":"blast","decimals":18},{"address":"","chain_id":"linea","decimals":18},{"address":"","chain_id":"manta-pacific","decimals":18}],"name":"Ethereum","symbol":"ETH"},"price":null,"quantity":{"decimals":18,"float":0.0000016785001958,"int":"1678500195825","numeric":"0.000001678500195825"},"value":null},"flags":{"is_trash":false},"hash":"0xbbcfb0ac5e466ded168794a162da334634fee3f95adfdb55392999f91b4c6d41","mined_at":"2024-07-31T00:17:35Z","mined_at_block":7490818,"nonce":250,"operation_type":"execute","sent_from":"0xfc0f1b3fb88c5ab19e77a6f7d4d637272e71e684","sent_to":"0x8286d601a0ed6cf75e067e0614f73a5b9f024151","status":"confirmed","transfers":[]},"id":"a65b2541c58a5908a7333480f0ac6792","relationships":{"chain":{"id":"linea","type":"chains"},"dapp":{"id":"","type":"dapps"}},"type":"transactions"}]}`
message := xTimestamp + "\n" + messageBody + "\n"
err = VerifySignature(x509Cert, message, xSignature)
assert.NoError(t, err)
}
Basic authentication header of the form Basic <encoded-value>, where <encoded-value> is the base64-encoded string username:password.
Callback URL where updates will be delivered to
"https://webhook.site/fcd606d2-f5bd-4832-9874-ff07c980b5a3"
Addresses of the wallets to create subscription for. Maximum 100 addresses per subscription. Note: Free tier accounts are limited to 5 addresses per subscription.
Address of the wallet.
Subscribe for transactions from specified chains. Leave empty to subscribe for all supported chains. You can find available chain ids in chain endpoints.
Response for requested wallet's transactions subscription