> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.withpersona.com/2020-05-18/workflows-best-practices/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.withpersona.com/_mcp/server. # Workflows Best Practices > Build secure, reliable Workflows for automating decisions and external requests. ## Check signatures for `Make HTTPS Request` steps Whenever you use a `Make HTTPS Request` step, you should configure the step to provide a `Persona-Signature` header with an HMAC for all POST requests. You can use this header to check that a request is authentic (i.e. it originates from Persona) and thus is safe to process. Compare this HMAC value with your own digest computed from the request body and a configurable secret value. You can configure the secret value by expanding the Advanced Configuration setting when creating a new `Make HTTPS Request` step. > **Check** > > If you are already authenticating requests for webhooks to this endpoint, you can reuse the same code to validate signatures. If you do, you should also set the `Make HTTPS Request` step secret to be the same as your webhook secret to ensure signatures continue to be validated properly. ![workflow-configuration](https://assets.withpersona.com/f_auto,q_auto/developer-docs/images/workflow-configuration.png) The `Persona-Signature` header contains two comma-separated key-value pairs encoding information about the request. The first key-value pair will be in the form `t=` and represents the unix time that the request was sent. The second key-value pair will be in the form `v1=`, where the signature is computed from the configured secret value and a dot-separated string composed of the unix timestamp joined with the request body. Sample code for checking signatures: **`ruby`** ```ruby ruby t, v1 = request.headers['Persona-Signature'].split(',').map { |value| value.split('=').second } computed_digest = OpenSSL::HMAC.hexdigest('SHA256', , "#{t}.#{request.body.read}") if v1 == computed_digest # Handle verified webhook event end ``` **`python`** ```python python t, v1 = [value.split('=')[1] for value in request.headers['Persona-Signature'].split(',')] computed_digest = hmac.new(.encode(), (t + '.' + request.data.decode('utf-8')).encode(), 'sha256').hexdigest() if hmac.compare_digest(v1, computed_digest): # Handle verified webhook event ``` **`javascript`** ```javascript javascript const sigParams = {} request.headers['Persona-Signature'] .split(',') .forEach(pair => { const [key, value] = pair.split('='); sigParams[key] = value; }) if (sigParams.t && sigParams.v1) { const hmac = crypto.createHmac('sha256', ) .update(sigParams.t + '.' + request.body) .digest('hex'); if (crypto.timingSafeEqual(Buffer.from(hmac), Buffer.from(sigParams.v1))) { // Handle verified webhook event } } ``` > **Warning** > > #### Parsing JSON when Computing HMACs > > In some languages, parsing the JSON may result in something that's not equivalent to the event body. For example, JavaScript may round floats and reduce precision. We recommend using the raw event body when computing the HMAC. ## Use Wait steps for shorter waits [Wait steps](/workflows#asynchronous-activity) can result in workflow runs that take a long time to process. In order to prevent long running workflow runs, wait steps have a 30 day limit for their resolution time. 1. **Wait on objects**: If the event(s) being waited on for the object do not occur within 30 days, the wait step will automatically resolve. 2. **Wait for time**: The maximum amount of time allowed is 30 days. > **Info** > > If you have scenarios where you need to wait longer than 30 days, we recommend creating a workflow with a Scheduled trigger, and using the `Schedule Workflow Run` action. ## Learn more See [Workflow Best Practices](https://help.withpersona.com/articles/63hzKuMfRsrD4AWYSQYmcF/) in our Help Center for more tips. > Build secure, reliable Workflows for automating decisions and external requests.