Portal Community

When to Use

Live Mockup

Approve Invoice INV-2026-0042

Invoice from Acme Supplies Ltd for $14,200 is awaiting your approval before payment is processed.
VendorAcme Supplies Ltd Amount$14,200.00 Due Date2026-06-15 PO ReferencePO-2026-0099

Request Payload Schema

interface ApprovalPayload {
  /** Human-readable explanation of what is being approved */
  context: string;

  /** Structured key-value fields presented in the approval UI */
  fields?: ApprovalField[];
}

interface ApprovalField {
  key:   string;   // internal key
  label: string;   // display label
  value: string;   // display value
}

Full Request Example

await sendInteraction({
  type: 'approval',
  targetUserId: 'usr_mgr_jane',
  title: 'Approve Invoice INV-2026-0042',
  payload: {
    context: 'Invoice from Acme Supplies Ltd for $14,200 requires your approval before payment is processed.',
    fields: [
      { key: 'vendor',    label: 'Vendor',       value: 'Acme Supplies Ltd' },
      { key: 'amount',    label: 'Amount',        value: '$14,200.00' },
      { key: 'due',       label: 'Due Date',      value: '2026-06-15' },
      { key: 'po',        label: 'PO Reference',  value: 'PO-2026-0099' }
    ]
  },
  timeoutMs: 86_400_000,   // 24 hours
  priority: 'high'
});

Response Schema

interface ApprovalResponse {
  interactionId: string;
  respondedBy:   string;
  outcome:       'approved' | 'rejected' | 'abstained';
  data: {
    comment?: string;   // optional comment from the approver
  };
  timestamp: string;   // ISO 8601
}

Response Example

{
  "interactionId": "int_01HXY4Z8KQ2W3V9G",
  "respondedBy":   "usr_mgr_jane",
  "outcome":       "approved",
  "data": {
    "comment": "Vendor terms checked — proceed with payment."
  },
  "timestamp": "2026-05-25T10:37:51Z"
}

Handling the Response

const response = await sendInteraction({ type: 'approval', ... });

switch (response.outcome) {
  case 'approved':
    await processPayment(invoice.id);
    notifyRequester('Your invoice has been approved by ' + response.respondedBy);
    break;
  case 'rejected':
    notifyRequester('Invoice rejected. Comment: ' + (response.data?.comment ?? 'none'));
    break;
  case 'abstained':
    // Re-route to next approver or escalate
    await escalateApproval(invoice.id, response.data?.comment);
    break;
}

Validation Rules

FieldRule
payload.contextRequired, max 1000 characters
payload.fieldsOptional, max 20 items per array
response.outcomeMust be one of: approved, rejected, abstained
response.data.commentOptional, max 2000 characters
Abstain vs. Reject Abstain means the approver declines to make a decision (not the same as rejecting). Use it when you want to support escalation paths — an abstain can trigger re-routing to another approver, while a rejection is a definitive no.