Skip to main content
A skill is a reusable, versioned rule set the Filed analyst applies automatically during a tax prep or tax advisor run. Skills are scoped to a workspace (WORKSPACE skills, shared firm protocols) or to a single user (USER skills, personal protocols). The Playbook screen in the Filed web app is the human UI over this API. This recipe is the minimal call sequence to list a workspace’s skills for a task family, share a personal skill with the firm, approve or deny that promotion, toggle a skill on or off, and scope which skills apply to a single run. Every call uses a workspaceToken (see Authentication) and goes to the single GraphQL endpoint:
https://router.apps.filed.com/graphql
This recipe does not re-document the types it touches. For the full Skill, SkillRule, SkillStrategy, SkillActivityEvent, and UserShortDetails field lists, the SkillKind and SkillStatus enums, and the deleteSkill mutation, see Skills.

1. List a workspace’s skills for a task type

There is no top-level skills query. Read workspace.skills through me { ... on WorkspaceUser { workspace { skills(...) } } } (see Skills). Pass taskType to scope the list to one task family, and showCuratedSkills: true to include Filed’s curated built-in skills alongside the workspace’s own.
query GetTaskSkills($taskType: String!) {
  me {
    ... on WorkspaceUser {
      id
      workspace {
        id
        skills(taskType: $taskType, showCuratedSkills: true) {
          kind
          taskType
          name
          description
          status
          returnType
          updatedAt
          owner {
            id
            name
            email
          }
        }
      }
    }
  }
}
{
  "taskType": "tax-prep"
}
curl -X POST https://router.apps.filed.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
  -d '{
    "query": "query GetTaskSkills($taskType: String!) { me { ... on WorkspaceUser { id workspace { id skills(taskType: $taskType, showCuratedSkills: true) { kind taskType name description status returnType updatedAt owner { id name email } } } } } }",
    "variables": { "taskType": "tax-prep" }
  }'
{
  "data": {
    "me": {
      "id": "019f0fb6-37b1-7800-b7bc-0d11288504b1",
      "workspace": {
        "id": "018f9c20-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
        "skills": [
          {
            "kind": "WORKSPACE",
            "taskType": "tax-prep",
            "name": "check-w2-totals",
            "description": "Verify W-2 wage totals against binder extractions.",
            "status": "APPROVED",
            "returnType": "F1040",
            "updatedAt": "2026-06-22T10:14:00.000Z",
            "owner": {
              "id": "019f0fb6-3001-7900-b7bc-0d11288504b1",
              "name": "Jane Preparer",
              "email": "jane@example-firm.com"
            }
          },
          {
            "kind": "USER",
            "taskType": "tax-prep",
            "name": "my-firm-reconciliation",
            "description": "Personal reconciliation protocol.",
            "status": "NONE",
            "returnType": null,
            "updatedAt": "2026-07-04T18:22:01.000Z",
            "owner": {
              "id": "019f0fb6-3001-7900-b7bc-0d11288504b1",
              "name": "Jane Preparer",
              "email": "jane@example-firm.com"
            }
          }
        ]
      }
    }
  }
}
Pick the USER skill you want to share with the firm. Save its taskType and name; the promotion mutations in steps 3 and 4 identify a skill by those two values (no kind argument).

2. Fetch one skill’s detail and activity

Before promoting a skill, read its full detail and activity timeline with workspace.skill. Identify the skill with kind + taskType + name (and optional returnType when the skill is scoped to a return type).
query GetSkill(
  $kind: SkillKind!
  $taskType: String!
  $name: String!
  $returnType: ReturnType
) {
  me {
    ... on WorkspaceUser {
      id
      workspace {
        id
        skill(
          kind: $kind
          taskType: $taskType
          name: $name
          returnType: $returnType
        ) {
          kind
          taskType
          name
          description
          body
          status
          returnType
          updatedAt
          owner {
            id
            name
            email
          }
          activity {
            action
            timestamp
            note
            triggeredBy {
              id
              name
              email
            }
          }
        }
      }
    }
  }
}
{
  "kind": "USER",
  "taskType": "tax-prep",
  "name": "my-firm-reconciliation"
}
curl -X POST https://router.apps.filed.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
  -d '{
    "query": "query GetSkill($kind: SkillKind!, $taskType: String!, $name: String!, $returnType: ReturnType) { me { ... on WorkspaceUser { id workspace { id skill(kind: $kind, taskType: $taskType, name: $name, returnType: $returnType) { kind taskType name description body status returnType updatedAt owner { id name email } activity { action timestamp note triggeredBy { id name email } } } } } } }",
    "variables": {
      "kind": "USER",
      "taskType": "tax-prep",
      "name": "my-firm-reconciliation"
    }
  }'
{
  "data": {
    "me": {
      "id": "019f0fb6-37b1-7800-b7bc-0d11288504b1",
      "workspace": {
        "id": "018f9c20-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
        "skill": {
          "kind": "USER",
          "taskType": "tax-prep",
          "name": "my-firm-reconciliation",
          "description": "Personal reconciliation protocol.",
          "body": "Flag any 1099-B where proceeds differ from the broker statement by more than $1, and surface the discrepancy as a review item.",
          "status": "NONE",
          "returnType": null,
          "updatedAt": "2026-07-04T18:22:01.000Z",
          "owner": {
            "id": "019f0fb6-3001-7900-b7bc-0d11288504b1",
            "name": "Jane Preparer",
            "email": "jane@example-firm.com"
          },
          "activity": [
            {
              "action": "created",
              "timestamp": "2026-07-04T18:22:01.000Z",
              "note": null,
              "triggeredBy": {
                "id": "019f0fb6-3001-7900-b7bc-0d11288504b1",
                "name": "Jane Preparer",
                "email": "jane@example-firm.com"
              }
            }
          ]
        }
      }
    }
  }
}
workspace.skill returns null when no skill matches the supplied kind + taskType + name (+ returnType). Handle null as a not-found result before you try to promote. See Skills, view a single skill.

3. Request promoting a personal skill to the workspace

requestSkillPromotion submits a USER skill for firm-wide review. The skill’s status moves from NONE to PENDING. A workspace admin then approves or denies it in step 4. The mutation identifies the skill by taskType + name only; there is no kind argument because only USER skills can be promoted.
mutation RequestSkillPromotion($taskType: String!, $name: String!) {
  requestSkillPromotion(taskType: $taskType, name: $name) {
    kind
    taskType
    name
    status
  }
}
{
  "taskType": "tax-prep",
  "name": "my-firm-reconciliation"
}
curl -X POST https://router.apps.filed.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
  -d '{
    "query": "mutation RequestSkillPromotion($taskType: String!, $name: String!) { requestSkillPromotion(taskType: $taskType, name: $name) { kind taskType name status } }",
    "variables": { "taskType": "tax-prep", "name": "my-firm-reconciliation" }
  }'
{
  "data": {
    "requestSkillPromotion": {
      "kind": "USER",
      "taskType": "tax-prep",
      "name": "my-firm-reconciliation",
      "status": "PENDING"
    }
  }
}

4. Approve or deny the promotion

A workspace admin resolves the PENDING promotion with one of two mutations. Both identify the skill by taskType + name and return the updated Skill. To approve, call approveSkillPromotion. The skill’s status becomes APPROVED and it applies firm-wide.
mutation ApproveSkillPromotion($taskType: String!, $name: String!) {
  approveSkillPromotion(taskType: $taskType, name: $name) {
    kind
    taskType
    name
    status
  }
}
{
  "taskType": "tax-prep",
  "name": "my-firm-reconciliation"
}
curl -X POST https://router.apps.filed.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
  -d '{
    "query": "mutation ApproveSkillPromotion($taskType: String!, $name: String!) { approveSkillPromotion(taskType: $taskType, name: $name) { kind taskType name status } }",
    "variables": { "taskType": "tax-prep", "name": "my-firm-reconciliation" }
  }'
{
  "data": {
    "approveSkillPromotion": {
      "kind": "USER",
      "taskType": "tax-prep",
      "name": "my-firm-reconciliation",
      "status": "APPROVED"
    }
  }
}
To deny, call denySkillPromotion. The skill’s status becomes DENIED and the optional reason is recorded on the activity timeline so the owner can see why the promotion was rejected.
mutation DenySkillPromotion(
  $taskType: String!
  $name: String!
  $reason: String
) {
  denySkillPromotion(taskType: $taskType, name: $name, reason: $reason) {
    kind
    taskType
    name
    status
  }
}
{
  "taskType": "tax-prep",
  "name": "my-firm-reconciliation",
  "reason": "Overlaps with existing firm protocol check-w2-totals."
}
curl -X POST https://router.apps.filed.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
  -d '{
    "query": "mutation DenySkillPromotion($taskType: String!, $name: String!, $reason: String) { denySkillPromotion(taskType: $taskType, name: $name, reason: $reason) { kind taskType name status } }",
    "variables": {
      "taskType": "tax-prep",
      "name": "my-firm-reconciliation",
      "reason": "Overlaps with existing firm protocol check-w2-totals."
    }
  }'
{
  "data": {
    "denySkillPromotion": {
      "kind": "USER",
      "taskType": "tax-prep",
      "name": "my-firm-reconciliation",
      "status": "DENIED"
    }
  }
}
After any of the promotion mutations, refetch GetTaskSkills and GetSkill so the Playbook UI reflects the new status. The web app calls client.refetchQueries({ include: ["GetTaskSkills", "GetSkill"] }) after every bulk action. See Skills, toggle a skill active or inactive.

5. Toggle a skill active or inactive

setSkillActive enables or disables a skill without deleting it. Disabling sets status: DISABLED so the skill stops applying during runs but is still listed and can be re-enabled. Pass returnType when the skill is scoped to a return type.
mutation SetSkillActive(
  $taskType: String!
  $name: String!
  $active: Boolean!
  $returnType: ReturnType
) {
  setSkillActive(
    taskType: $taskType
    name: $name
    active: $active
    returnType: $returnType
  ) {
    kind
    taskType
    name
    status
  }
}
{
  "taskType": "tax-prep",
  "name": "check-w2-totals",
  "active": false,
  "returnType": "F1040"
}
curl -X POST https://router.apps.filed.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
  -d '{
    "query": "mutation SetSkillActive($taskType: String!, $name: String!, $active: Boolean!, $returnType: ReturnType) { setSkillActive(taskType: $taskType, name: $name, active: $active, returnType: $returnType) { kind taskType name status } }",
    "variables": {
      "taskType": "tax-prep",
      "name": "check-w2-totals",
      "active": false,
      "returnType": "F1040"
    }
  }'
{
  "data": {
    "setSkillActive": {
      "kind": "WORKSPACE",
      "taskType": "tax-prep",
      "name": "check-w2-totals",
      "status": "DISABLED"
    }
  }
}
Re-enable the same skill by calling setSkillActive again with active: true; its status returns to APPROVED.
There is no runSkill mutation. Skills are never invoked directly. The analyst applies every APPROVED (active) skill automatically during a triggerTaxPrep or triggerTaxAdvisor run. The next section shows how to scope which active skills apply to one specific run.

6. Scope which skills apply to a single run

Because skills apply automatically based on their status, the way to control which skills a particular run uses is the optional skills argument (RunSkillSelectionInput) on the trigger mutations: RunSkillSelectionInput takes two optional lists of skill names:
input RunSkillSelectionInput {
  workspace: [String!]
  user: [String!]
}
Omit skills entirely to apply all active skills in both scopes. Pass an empty list in one scope to censor every skill in that scope. Pass a non-empty list to apply only those named skills. For example, to start a tax prep run that applies only the workspace skill check-w2-totals and the user skill my-firm-reconciliation, and no others:
mutation TriggerTaxPrep($input: TriggerTaxPrepInput!) {
  triggerTaxPrep(input: $input) {
    taskId
  }
}
{
  "input": {
    "clientId": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c",
    "returnType": "F1040",
    "skills": {
      "workspace": ["check-w2-totals"],
      "user": ["my-firm-reconciliation"]
    }
  }
}
curl -X POST https://router.apps.filed.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
  -d '{
    "query": "mutation TriggerTaxPrep($input: TriggerTaxPrepInput!) { triggerTaxPrep(input: $input) { taskId } }",
    "variables": {
      "input": {
        "clientId": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c",
        "returnType": "F1040",
        "skills": {
          "workspace": ["check-w2-totals"],
          "user": ["my-firm-reconciliation"]
        }
      }
    }
  }'
{
  "data": {
    "triggerTaxPrep": {
      "taskId": "018f9c2c-4a1b-7e20-8b33-7c4d5e6f7080"
    }
  }
}
Poll the returned taskId per Run tax prep end to end and Tasks. The selected skills shape the review items the analyst raises during the run.

Next steps

  • For the full Skill, SkillRule, SkillStrategy, SkillActivityEvent, and UserShortDetails field lists, the SkillKind and SkillStatus enums, and the deleteSkill mutation, see Skills.
  • To run tax prep with the active skills and read the resulting review items, see Run tax prep end to end and Tax prep.
  • To run tax planning with the active skills and read the resulting strategies, see Run tax planning / advisory and Tax planning.