Private BetaProposeFlow is currently in private beta.Join the waitlist

Managing Decisions

Learn how to capture and manage user decisions on proposals using the SDK.

Decision API

The proposals.decide() method records the user's decision on a proposal.

decide.ts
// Approve a proposal
const decision = await pf.proposals.decide(proposalId, {
  action: 'approve',
});

// Reject a proposal
const decision = await pf.proposals.decide(proposalId, {
  action: 'reject',
  reason: 'Content needs to be more technical',
});

Decision Options

options.ts
interface DecideInput {
  // Required: The decision action
  action: 'approve' | 'reject';

  // Optional: Partial edits to apply
  // For approvals: merged into finalObject
  // For rejections: become constraints for regeneration
  edits?: Partial<T>;

  // Optional: Reason for the decision
  reason?: string;

  // Optional: Additional metadata
  metadata?: Record<string, unknown>;
}

// Response structure
interface Decision {
  id: string;
  proposalId: string;
  action: 'approve' | 'reject';
  edits: Record<string, unknown> | null;
  finalObject: Record<string, unknown> | null;
  reason: string | null;
  metadata: Record<string, unknown>;
  createdAt: string;
}

Approve with Edits

Users can make modifications before approving. Pass partial edits that get merged with the original.

edit.ts
// Fetch the proposal
const proposal = await pf.proposals.get(proposalId);

// User wants to change just the title
const decision = await pf.proposals.decide(proposalId, {
  action: 'approve',
  edits: {
    title: 'Better Title by User',
  },
});

// decision.finalObject contains the merged result
console.log(decision.finalObject);
// { title: 'Better Title by User', content: '...original...', tags: [...] }

Reject with Edits

When rejecting, you can include edits that become constraints for the next regeneration. This enables iterative refinement where users lock in the parts they like.

reject-edits.ts
// User likes the recipe but wants 4 portions instead of 2
const decision = await pf.proposals.decide(proposalId, {
  action: 'reject',
  reason: 'Need more portions',
  edits: { portions: 4 },  // Will be used as constraint
});

// Regenerate - edits automatically become constraints
const { proposal: improved } = await pf.proposals.regenerate(proposalId, {});

// The new proposal will have portions=4, with other fields
// adjusted accordingly (e.g., ingredient quantities scaled up)

Tracking Users

Track which user made each decision for audit purposes.

user-tracking.ts
const decision = await pf.proposals.decide(proposalId, {
  action: 'approve',
  metadata: {
    decidedBy: currentUser.id,
    userEmail: currentUser.email,
    ipAddress: request.ip,
  },
});

console.log(decision.metadata.decidedBy);  // 'user_123'
console.log(decision.createdAt);           // '2024-01-15T10:30:00Z'

Querying Decisions

query.ts
// List proposals by status
const { data: approved } = await pf.proposals.list({
  status: 'approved',
  limit: 50,
});

const { data: rejected } = await pf.proposals.list({
  status: 'rejected',
});

// Each proposal includes its decision
for (const proposal of approved) {
  console.log(proposal.decision?.finalObject);
}

// Filter by schema
const { data: blogPosts } = await pf.proposals.list({
  status: 'approved',
  schemaId: 'sch_abc123',
});

Webhook Events

Set up webhooks to receive real-time notifications when decisions are made.

webhook.ts
// Register a webhook endpoint
await pf.webhooks.create({
  url: 'https://your-app.com/webhooks/proposeflow',
  events: ['proposal.approved', 'proposal.rejected'],
  secret: 'whsec_...',
});

// Your webhook handler receives events like:
// {
//   type: 'proposal.approved',
//   data: {
//     proposal: { id, schemaName, generatedObject, ... },
//     decision: { id, action, finalObject, ... }
//   }
// }