flowawsome@flowaws/node-sdk

Guides

Patterns for things your backend decides, built on the public API and webhooks.

Route approvals by amount

flowawsome does not receive amounts. Your backend computes the amount (tax, currency and cumulative rules are yours) and picks the approval chain by sending a different resourceType per amount tier. Each tier has its own approval definition.

  1. Create one definition per tier, each with its own resourceType, for example purchase_order.tier_1, purchase_order.tier_2 and purchase_order.tier_3.
  2. Map the amount to a tier in your code and route with that type.
  3. Check instance.resolvedFrom.matchedResourceType in the response. It should equal the type you sent.
// Thresholds live in your code: changing them is a deploy of your service.
function tierFor(amount: number): string {
  if (amount < 1_000_000) return 'purchase_order.tier_1';
  if (amount < 10_000_000) return 'purchase_order.tier_2';
  return 'purchase_order.tier_3';
}

const resourceType = tierFor(order.totalAmount);
const res = await fetch(
  `https://api.flowawsome.com/services/${serviceId}/workflow-instances`,
  {
    method: 'POST',
    headers: {
      'X-Api-Key': process.env.FLOWAWSOME_API_KEY!,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      resourceType,
      resourceId: order.id,
      initiatorUserId: order.requesterUserId,
      // A resubmission after the amount changes is a new request: use a new key.
      idempotencyKey: `${order.id}-submit-${order.revision}`,
    }),
  },
);

const { instance } = await res.json();
if (instance.resolvedFrom?.matchedResourceType !== resourceType) {
  // A definition without a resourceType (a catch-all) won instead of the tier.
  console.warn('tier definition not used', instance.resolvedFrom);
}

Catch-all definitions. A definition without a resourceType matches every type. Resolution picks the most specific scope first (individual, department, service, app, organization) and only then compares definitions, so a catch-all at a department scope beats a tier definition at the organization scope. Within one scope a higher specificityRank wins, then the older definition. Keep tier definitions at the same scope as any catch-all and give them a higher rank, or avoid catch-alls where tiered requests resolve.

Remind and escalate a step nobody has acted on

flowawsome has no approval deadlines. Your backend keeps the clock: start a timer when a step starts waiting, update it as approvers act, and stop it when the request moves on or ends.

  1. Register a webhook endpoint with the step events. They are opt-in: an endpoint registered without subscribedEvents receives only the three instance-level events.
  2. On workflow_step.pending, start a timer for data.step.workflowStepId with the waiting approvers from data.step.approverUserIds.
  3. On workflow_step.actioned, remove data.step.actionedByUserId from that step's waiting list. A parallel step can still be waiting on others.
  4. On workflow_step.reassigned, replace the waiting list with the new approverUserIds and restart the timer.
  5. Stop the timer when a workflow_step.pending arrives for a later stepIndex of the same instance, or on workflow_instance.approved / workflow_instance.rejected.
curl -X POST https://api.flowawsome.com/workflows/webhook-endpoints \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "orgId": "'$ORG_ID'",
    "url": "https://example.com/flowawsome/webhooks",
    "subscribedEvents": [
      "workflow_step.pending",
      "workflow_step.actioned",
      "workflow_step.reassigned",
      "workflow_instance.approved",
      "workflow_instance.rejected"
    ]
  }'

When a timer fires, remind the people still on the waiting list, then escalate in your own channel (their manager, an operations queue). To move the step to someone else, an organization admin uses PATCH /workflows/instances/{instanceId}/steps/{stepId}/approver with approverUserId and a reason, or the Approvals screen in the admin console. That call takes an admin's bearer token, not an API key, and it sends workflow_step.reassigned, which restarts your timer.

Payload fields are in the Workflow API reference. Signature verification is in the Quickstart.