Callback or webhook: which signal moves your embedded flow forward
You have embedded signing in your product: the signer opens a page in your app, the IgniSign iframe loads inside it, and they sign without leaving. Then two things happen almost at once. The iframe tells your page that the signer is done, and IgniSign sends a webhook to your backend. Both say "signed". The decision for the integrating developer is which of the two signals your product acts on, and for what.
Two signals, two audiences
The browser SDK (@ignisign/ignisign-js) renders the signing interface in an iframe inside an element of your page. When you start a signature session, you pass callbacks. The iframe posts messages to the parent window, and the SDK turns them into calls: one when the signer has finished, with the signature request id, the signer id and the signature ids; one when the session hits an error, with an error code and its context.
The webhook is a separate channel. IgniSign calls your backend endpoint with an event for the signature request, carrying the application id, the environment and a verification token that your backend confirms with the API.
The first signal is addressed to the screen the user is looking at. The second is addressed to the system that owns the record.
What the callback is good for
The callback is immediate and local. Use it for what the signer sees next:
- close or hide the signing panel (with
closeOnFinish, which is on by default, the iframe closes itself); - show a "thank you, we are processing your signature" state;
- move the user to the next screen of your flow;
- on the error callback, show a clear message and offer to try again.
What the callback is not: evidence that the signature exists. It runs in the user's browser. A closed tab or a lost connection means it may never fire. And anything your backend does because a browser said so can be triggered by anyone who can call your endpoint from a browser.
What the webhook is good for
The webhook reaches your backend whether the signer's tab is still open or not. Use it for every state change that matters:
- mark the contract, mandate or onboarding step as signed;
- unlock the account, the payment or the next document;
- fetch the signed file and the proof your product keeps.
Confirm each event's verification token before you act, and make the handler idempotent on the signature request id. IgniSign keeps an event log for your application, and you can resend an event from it by hand if your endpoint missed it, so the same event may arrive twice.
Joining the two
The gap between the signals is usually short, but it is a gap, and your page has to handle it. A pattern that holds up:
- The finalized callback switches the page to a pending state and stops there. It does not call an endpoint that changes data.
- The backend receives the webhook, verifies it and updates the record.
- The page learns the result from your own backend: a short poll of the record's status, or the push channel your app already uses.
- If the backend has not seen the event after a while, the page keeps its pending message and your team checks the event log, rather than letting the browser force the state.
The error callback follows the same split. It decides what the signer sees; the record stays as the backend last knew it.
This split has a side benefit. In BySide mode there is no callback in your page at all, so the webhook is already the only signal. Flows written with the backend as the single place where state changes keep the same business logic whichever mode a flow uses.
A quick test for your handlers
Read each callback you wrote and ask one question: if this function never ran, would any record be wrong? If the answer is yes, that logic belongs in the webhook handler. If the answer is no, the callback is doing its job.
The decision
Let the callback drive the screen and the webhook drive the data. The signer gets an immediate response inside your product, and your records change only when IgniSign has told your backend, in a way you can verify, that the signature happened.
Run the whole loop in your development environment before you use production keys: open the iframe, sign, watch the callback update the page, then watch the webhook update the record.