Skip to main content
A skill is a reusable, versioned rule set that the Filed analyst applies during a 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. Skill operations are reached through the me query resolved as a WorkspaceUser, so every read and mutation on this page requires a workspaceToken (see Authentication). All requests go to:
https://router.apps.filed.com/graphql
There is no top-level skills or skill query. Both are fields on Workspace, reached through me { ... on WorkspaceUser { workspace { skills(...) } } }. The workspaceToken already identifies which workspace, so you never pass a workspace ID.

The Skill type

type Skill {
  kind: SkillKind!
  taskType: String!
  name: String!
  description: String!
  body: String
  updatedAt: String!
  createdAt: String!
  returnType: ReturnType
  applicableWhen: String
  rules: [SkillRule!]
  strategies: [SkillStrategy!]
  status: SkillStatus
  owner: UserShortDetails
  activity: [SkillActivityEvent!]
}

enum SkillKind {
  WORKSPACE
  USER
}

enum SkillStatus {
  NONE
  PENDING
  APPROVED
  DENIED
  DISABLED
}

enum ReturnType {
  F1040
  F1041
  F1065
  F1120
  F1120S
  F990
}
kind
SkillKind!
The skill’s scope: WORKSPACE (shared firm protocol) or USER (personal protocol). Determines who can edit, promote, and delete it.
taskType
String!
The task family this skill applies to, for example tax-prep or tax-advisor. Skills are grouped and listed by taskType.
name
String!
The skill’s unique name within its taskType and kind. Together kind + taskType + name (+ optional returnType) identifies a single skill.
description
String!
A short human-readable summary of what the skill does.
body
String
The full rule body (the prompt / instruction text the analyst applies). May be empty for curated skills.
updatedAt
String!
ISO 8601 timestamp of the last edit.
createdAt
String!
ISO 8601 timestamp of creation.
returnType
ReturnType
When set, the skill only applies to clients of this return type (F1040, F1041, F1065, F1120, F1120S, F990). When null, the skill applies to all return types.
applicableWhen
String
A free-text condition describing when the skill should fire. Display only.
rules
[SkillRule!]
The structured rules attached to the skill. See the SkillRule type.
strategies
[SkillStrategy!]
The strategies attached to the skill. See the SkillStrategy type.
status
SkillStatus
The skill’s lifecycle state: NONE, PENDING, APPROVED, DENIED, or DISABLED. PENDING means a USER skill has been shared with the firm and is awaiting approval; APPROVED/DENIED are the resolved promotion states; DISABLED means an admin has turned it off without deleting it.
owner
UserShortDetails
The user who owns the skill. See UserShortDetails.
activity
[SkillActivityEvent!]
The skill’s activity timeline (promotions, approvals, activations, edits). See SkillActivityEvent.

The SkillRule type

type SkillRule {
  id: String!
  severity: String!
  category: String!
  domain: String!
  tolerance: Int
  titleTemplate: String
}
id
String!
The rule’s unique identifier within the skill.
severity
String!
The severity the rule raises when it fires (for example critical, high, medium, low).
category
String!
The review category the rule maps to (for example data-entry, reconciliation).
domain
String!
The knowledge domain the rule belongs to.
tolerance
Int
An optional numeric tolerance the rule allows before flagging.
titleTemplate
String
A template string used to render the rule’s title in review output.

The SkillStrategy type

type SkillStrategy {
  id: String!
  titleTemplate: String
}
id
String!
The strategy’s unique identifier within the skill.
titleTemplate
String
A template string used to render the strategy’s title in planning output.

The SkillActivityEvent type

type SkillActivityEvent {
  action: String!
  timestamp: String!
  triggeredBy: UserShortDetails
  note: String
}
action
String!
What happened, for example created, updated, shared, approved, denied, enabled, disabled. The web app humanizes this by replacing hyphens and underscores with spaces and title-casing the result.
timestamp
String!
ISO 8601 timestamp of the event.
triggeredBy
UserShortDetails
The user who triggered the event. See UserShortDetails.
note
String
An optional human-readable note attached to the event (for example the denial reason from denySkillPromotion).

The UserShortDetails type

UserShortDetails is a federated entity. The ai subgraph declares it with only id; the platform subgraph resolves the human-readable fields.
type UserShortDetails {
  id: ID!
  name: String!
  email: String!
  emailHash: String
}
id
ID!
The user’s account ID.
name
String!
The user’s display name.
email
String!
The user’s email.
emailHash
String
An optional hash of the email (used for avatars).

List skills

Read workspace.skills to list skills for a task family. The web app’s Playbook screen calls this with showCuratedSkills: true to include Filed’s built-in curated 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
          }
        }
      }
    }
  }
}

Arguments

skills(
  showCuratedSkills: Boolean = false
  taskType: String
  returnType: ReturnType
): [Skill!]!
showCuratedSkills
Boolean
When true, include Filed’s curated (built-in) skills in the result alongside the workspace’s own. Defaults to false.
taskType
String
Filter to one task family, for example tax-prep or tax-advisor. Omit to list skills across all task families.
returnType
ReturnType
Filter to skills that apply to a specific return type. Omit to list skills that apply to all return types.
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": "PENDING",
            "returnType": null,
            "updatedAt": "2026-07-04T18:22:01.000Z",
            "owner": {
              "id": "019f0fb6-3001-7900-b7bc-0d11288504b1",
              "name": "Jane Preparer",
              "email": "jane@example-firm.com"
            }
          }
        ]
      }
    }
  }
}

View a single skill

Read workspace.skill to fetch one skill’s full detail, including its body, rules, strategies, and activity timeline. Identify the skill with kind + taskType + name (and optional returnType).
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
            }
          }
        }
      }
    }
  }
}

Arguments

skill(
  kind: SkillKind!
  taskType: String!
  name: String!
  returnType: ReturnType
): Skill
kind
SkillKind!
required
WORKSPACE or USER.
taskType
String!
required
The task family, for example tax-prep.
name
String!
required
The skill’s name within its taskType and kind.
returnType
ReturnType
When the skill is scoped to a return type, pass it to disambiguate. Omit for skills that apply to all return types.
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": "WORKSPACE",
      "taskType": "tax-prep",
      "name": "check-w2-totals",
      "returnType": "F1040"
    }
  }'
{
  "data": {
    "me": {
      "id": "019f0fb6-37b1-7800-b7bc-0d11288504b1",
      "workspace": {
        "id": "018f9c20-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
        "skill": {
          "kind": "WORKSPACE",
          "taskType": "tax-prep",
          "name": "check-w2-totals",
          "description": "Verify W-2 wage totals against binder extractions.",
          "body": "Flag any W-2 where the extracted wage total differs from the source document by more than $1.",
          "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"
          },
          "activity": [
            {
              "action": "created",
              "timestamp": "2026-06-10T09:00:00.000Z",
              "note": null,
              "triggeredBy": {
                "id": "019f0fb6-3001-7900-b7bc-0d11288504b1",
                "name": "Jane Preparer",
                "email": "jane@example-firm.com"
              }
            },
            {
              "action": "approved",
              "timestamp": "2026-06-22T10:14:00.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.

The promotion workflow

A USER skill starts as a personal protocol visible only to its owner. To share it with the whole firm, the owner requests a promotion; a workspace admin then approves or denies it. On approval the skill becomes a WORKSPACE skill (or its WORKSPACE counterpart is activated) and applies for every user in the workspace. The three mutations below drive that flow. All require a workspaceToken.

Request a promotion

requestSkillPromotion submits a USER skill for firm-wide review. Its status becomes PENDING.
mutation RequestSkillPromotion($taskType: String!, $name: String!) {
  requestSkillPromotion(taskType: $taskType, name: $name) {
    kind
    taskType
    name
    status
  }
}
taskType
String!
required
The skill’s task family.
name
String!
required
The skill’s name.

Returns: Skill!

The promoted Skill with its updated status.
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"
    }
  }
}

Approve a promotion

approveSkillPromotion approves a PENDING skill. Its status becomes APPROVED and it applies firm-wide.
mutation ApproveSkillPromotion($taskType: String!, $name: String!) {
  approveSkillPromotion(taskType: $taskType, name: $name) {
    kind
    taskType
    name
    status
  }
}
taskType
String!
required
The skill’s task family.
name
String!
required
The skill’s name.

Returns: Skill!

The approved Skill with status: APPROVED.
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"
    }
  }
}

Deny a promotion

denySkillPromotion denies a PENDING skill. Its status becomes DENIED and the optional reason is recorded on the activity timeline.
mutation DenySkillPromotion(
  $taskType: String!
  $name: String!
  $reason: String
) {
  denySkillPromotion(taskType: $taskType, name: $name, reason: $reason) {
    kind
    taskType
    name
    status
  }
}
taskType
String!
required
The skill’s task family.
name
String!
required
The skill’s name.
reason
String
An optional denial note. Stored on the SkillActivityEvent so the owner can see why the promotion was rejected.

Returns: Skill!

The denied Skill with status: DENIED.
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"
    }
  }
}

Toggle a skill active or inactive

setSkillActive enables or disables a skill without deleting it. Disabling sets status: DISABLED so the skill stops applying 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
String!
required
The skill’s task family.
name
String!
required
The skill’s name.
active
Boolean!
required
true to enable, false to disable.
returnType
ReturnType
When the skill is scoped to a return type, pass it to disambiguate. Omit for skills that apply to all return types.

Returns: Skill!

The updated Skill. Its status reflects the new active state (APPROVED when enabled, DISABLED when disabled).
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"
    }
  }
}

Delete a skill

deleteSkill permanently removes a skill. For WORKSPACE skills this deletes the skill for the whole firm (curated skills revert to their default). For USER skills this deletes the personal protocol. The mutation returns true on success.
deleteSkill is irreversible. For a WORKSPACE skill it removes the protocol for every user in the firm. Prefer setSkillActive with active: false when you only need to turn a skill off.
mutation DeleteSkill(
  $kind: SkillKind!
  $taskType: String!
  $name: String!
  $returnType: ReturnType
) {
  deleteSkill(
    kind: $kind
    taskType: $taskType
    name: $name
    returnType: $returnType
  )
}
kind
SkillKind!
required
WORKSPACE or USER.
taskType
String!
required
The skill’s task family.
name
String!
required
The skill’s name.
returnType
ReturnType
When the skill is scoped to a return type, pass it to disambiguate. Omit for skills that apply to all return types.

Returns: Boolean!

true when the skill was deleted.
curl -X POST https://router.apps.filed.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
  -d '{
    "query": "mutation DeleteSkill($kind: SkillKind!, $taskType: String!, $name: String!, $returnType: ReturnType) { deleteSkill(kind: $kind, taskType: $taskType, name: $name, returnType: $returnType) }",
    "variables": {
      "kind": "USER",
      "taskType": "tax-prep",
      "name": "my-firm-reconciliation"
    }
  }'
{
  "data": {
    "deleteSkill": true
  }
}
After any skill mutation, 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.