Silverfin supports a range of webhook events which allow you to track changes in your environment, and can function as a trigger for your own API flows based on such an event.
You subscribe to events by listing them in events when you register a webhook. A webhook's list of events can't be changed: to add or remove an event, destroy the webhook and register a new one with the full list.
Currently the API supports the following webhook events:
| Event | Description |
|---|---|
account.created | an account has been created |
account.updated | an account has been updated |
account.destroyed | an account has been deleted |
transaction_document.processed | a document is processed attached to a booking (coming from the accountancy system) |
transaction_document.requested | an import left the company with transaction documents awaiting upload (Sync API partners only) |
permanent_document.processed | a document is processed uploaded in the Silverfin documents section |
permanent_entry.created | a permanent document, folder or text has been created |
permanent_entry.updated | a permanent document, folder or text has been updated |
permanent_entry.destroyed | a permanent document, folder or text has been deleted |
permanent_entry.restored | a permanent document, folder or text has been restored after it was deleted |
comment.created | a new comment is created (currently limited to remarks only, but will be extended to checks as well) |
comment.updated | a comment has been updated |
comment.destroyed | a comment has been deleted |
remark.created | a new remark is created (can be on any object within Silverfin: company, reconciliation, report, ...) |
remark.updated | a remark has been updated |
remark.destroyed | a remark has been deleted |
custom.updated | a custom property on the company is created or updated. It does not fire for custom data on reconciliations, accounts, periods or permanent texts. |
period.created | when a period is unarchived |
period.updated | when a period is changed from private to public or from public to private |
period.destroyed | when a period is archived |
sync_state.updated | when a data synchronisation completes or fails, the payload contains if the sync has changed anything |
financial_balances.changed | when there are changes to financial data that might change the trial balances |
company.account_mapping_list.changed | when the company's account mapping list changes |
company.mapping.updated | when the mapping of accounts in the company changes |
export.created | when a new export PDF is generated |
export.destroyed | when an export has been deleted |
starrings.created | when a reconciliation or an account is starred |
starrings.destroyed | when a reconciliation or an account is unstarred |
The endpoint will send a POST request with a JSON payload to a specified target URL. It will always include the webhook, event, company and firm sections. Depending on the event, it contains one more section, described below. The document section in the example below is specific to events related to documents.
{"webhook":
{
"id": 1052767521,
"url": "http://localhost:3001/webhook",
"events": ["transaction_document.processed", "permanent_document.processed"]
},
"event": "permanent_document.processed",
"company":
{
"id": 398393022,
"name": "Demo Company"
},
"firm":
{
"country": BE,
"id": 71238,
"name": "Demo Firm"
},
"document":
{
"id": 147764866,
"file_name": "permanent_test_file.docx",
"content": "This is the content of the file",
"download_url": "https://live.getsilverfin.com/url_of_the_actual_file",
"permanent_document_path": ["Contract", "A private document"],
"permanent_document_id": 411704894
}
}
starrings.created and starrings.destroyed
starrings.created and starrings.destroyed| Field | Content |
|---|---|
starrable_type | reconciliation_text or account |
starrable | the reconciliation or account, in the same format as the API returns it |
ledger_ids | the IDs of the periods whose starring changed |
Starring a reconciliation or account in one period also changes the later periods that are not locked. ledger_ids lists all of them. When a new period is created and inherits stars, the event lists only that new period.
permanent_entry.*
permanent_entry.*| Field | Content |
|---|---|
permanent_entry.id | ID of the document, folder or text |
permanent_entry.name | its name |
permanent_entry.type | its type, for example PermanentDocument |
permanent_entry.content_updated_at | when it was last updated |
permanent_entry.visibility | public or private |
permanent_entry.created_by | the user who created it |
permanent_entry.document | for documents only: the document details |
permanent_entry.children | for destroyed and restored only: the entries inside it that were deleted or restored with it (id, type, name) |
Permanent entries are the same for every period, so there is no period in the payload.
company.account_mapping_list.changed
company.account_mapping_list.changedNo extra fields. Read the company's account mapping again when you receive it.
company.mapping.updated
company.mapping.updated| Field | Content |
|---|---|
account_ids | the IDs of the accounts whose mapping changed |
transaction_document.requested
transaction_document.requestedFor Sync API partners only. It fires only for companies synced through the Sync API.
It carries no document list. Fetch the list with GET /syncs/companies/{company_id}/documents, which may already be empty if you emptied it in the meantime.
Silverfin combines events per import: batches you push while an import is still running share one event. Small batches pushed with gaps between them each count as an import of their own, so each one notifies. The event repeats on your next import for as long as any reference stays unresolved, so resolve every reference the endpoint returns: upload the document, or flag it with DELETE /syncs/companies/{company_id}/document when no document exists. References left neither uploaded nor flagged keep the event firing.
Webhook signature verification
Every request must include two signature headers: X-SF-SIGNATURE-1 and X-SF-SIGNATURE-2. These signatures are generated using your application's webhook_signing_token_1 and webhook_signing_token_2, respectively.
To confirm the authenticity of a request originating from Silverfin, you should compute its Hash-based Message Authentication Code (HMAC) using the SHA-256 digest algorithm, the request's body as data, and a webhook signing token as the key. For instance, in Ruby, you can achieve this as shown below:
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), WEBHOOK_SIGNING_TOKEN, REQUEST_BODY)
If at least one of the computed signatures matches its respective header, you can consider the request as authentic. Matching with a single signature allows for a seamless rotation of your application's webhook signing tokens.
Retry strategy
When the endpoint doesn't reply with a 2xx code, the post will be retried with an exponential back-off over a total duration of ~48 hours. If after this time the endpoint still doesn't reply with a 2xx code, the webhook subscription will be marked as expired (disabled). Also note that an expired webhook subscription can't be revived, only deleted. To restore its functionality, the subscription will need to be re-created anew.
Throttling
Silverfin does not throttle outbound webhook events. To protect your application from occasional spikes in incoming events, you may wish to build your own solution or use a third-party service.
Timeouts
Silverfin requires the webhook receiving server to reply within a reasonable time. If the server takes too long to respond, the webhook event will be failed and be retried according to the Retry strategy described above. To avoid timeouts, you should defer processing of the payload until the server replies

