Approvals
Create custom approval flows on-the-fly.
Tasks support two ways to model approval flows:
- Before running: Restrict who can execute a task and require others to issue an execution request. To learn more, see Permissions.
- While running: Dynamically require approvals at runtime via prompt reviewers.
Prompt reviewers
With Prompts, you can gather input from operators through parameter forms. By
default, anyone who can access the associated run can also respond to the prompt, but this can be
locked down using the optional
reviewers
field.To give an example, you could enforce that someone from the
eng-managers
group approves a
high-risk deployment:typescriptCopied1await airplane.prompt.confirm({2description: "Deploying change to the **production** cluster.",3reviewers: {4groups: ["eng-managers"],5// Also accepts a list of users:6// users: ["jill@company.com", "jack@company.com"],7},8});
javascriptCopied1await airplane.prompt.confirm({2description: "Deploying change to the **production** cluster.",3reviewers: {4groups: ["eng-managers"],5// Also accepts a list of users:6// users: ["jill@company.com", "jack@company.com"],7},8});
pythonCopied1airplane.prompt(2description="Deploying change to the **production** cluster.",3reviewers=airplane.PromptReviewers(4groups=["eng-managers"],5# Also accepts a list of users:6# users: ["jill@company.com", "jack@company.com"],7),8)
This will render a prompt within the run's UI:

Groups are referenced by their slug which can be found in-app within the
groups page. Users are referenced by their email
address.
Self approvals
If you need to prevent a user from approving prompts created by a task they executed, you can
disable self-approvals.
This allows us to modify the example above such that an engineering manager cannot approve their own
deployment without the approval of another engineering manager.
typescriptCopied1await airplane.prompt.confirm({2description: "Deploying change to the *production* cluster.",3reviewers: {4allowSelfApprovals: false,5groups: ["eng-managers"],6// Also accepts a list of users:7// users: ["jill@company.com", "jack@company.com"],8},9});
javascriptCopied1await airplane.prompt.confirm({2description: "Deploying change to the *production* cluster.",3reviewers: {4allowSelfApprovals: false,5groups: ["eng-managers"],6// Also accepts a list of users:7// users: ["jill@company.com", "jack@company.com"],8},9});
pythonCopied1airplane.prompt(2description="Deploying change to the **production** cluster.",3reviewers=airplane.PromptReviewers(4allow_self_approvals=False,5groups=["eng-managers"],6# Also accepts a list of users:7# users: ["jill@company.com", "jack@company.com"],8),9)
Conditional approvals
Approvals can be made conditional by wrapping them in an
if
clause. For example, you could model a
support team's refund task where support is empowered to issue refunds immediately if the amount is
below a certain threshold:typescriptCopied1export default airplane.task(2{3slug: "issue_refund_conditionally",4parameters: {5transaction_id: "shorttext",6},7},8async (params) => {9const transaction = await getTransaction(params.transaction_id);1011// If this refund is above a certain threshold, ensure we get a manager's approval12// before issuing it. For documentation purposes, the manager must also provide a13// reason for approving the refund.14let reason = "";15if (transaction.amount > 30) {16({ reason } = await airplane.prompt(17{18reason: {19type: "shorttext",20name: "Refund reason",21description: "This refund must be approved by a manager with an explanation.",22},23},24{25reviewers: {26groups: ["managers"],27},28}29));30}3132await airplane.execute("issue_refund", {33transaction,34reason,35});36}37);
javascriptCopied1export default airplane.task(2{3slug: "issue_refund_conditionally",4parameters: {5transaction_id: "shorttext",6},7},8async (params) => {9const transaction = await getTransaction(params.transaction_id);1011// If this refund is above a certain threshold, ensure we get a manager's approval12// before issuing it. For documentation purposes, the manager must also provide a13// reason for approving the refund.14let reason = "";15if (transaction.amount > 30) {16({ reason } = await airplane.prompt(17{18reason: {19type: "shorttext",20name: "Refund reason",21description: "This refund must be approved by a manager with an explanation.",22},23},24{25reviewers: {26groups: ["managers"],27},28}29));30}3132await airplane.execute("issue_refund", {33transaction,34reason,35});36}37);
pythonCopied1import airplane2# For versions of Python prior to 3.9, `Annotated` can be imported from the `typing_extensions` package.3from typing import Annotated45@airplane.task()6def issue_refund_conditionally(transaction_id: str):7transaction = get_transaction(transaction_id)89# If this refund is above a certain threshold, ensure we get a manager's approval10# before issuing it. For documentation purposes, the manager must also provide a11# reason for approving the refund.12reason = ""13if transaction.amount > 30:14values = airplane.prompt(15{16"reason": Annotated[17str,18ParamConfig(19name="Refund reason",20description="This refund must be approved by a manager with an explanation.",21),22],23},24reviewers=airplane.PromptReviewers(groups=["managers"]),25)26reason = values["reason"]2728airplane.execute(29"issue_refund",30{31"transaction": transaction,32"reason": reason,33},34)