← Plugin catalog
Business & Operations

Wingspan

Wingspan v1.0.0

Publisher description

From the marketplace listing

Wingspan is the AI-native platform for the flexible workforce, built for finance and operations teams that manage and pay 1099 contractors, international contractors, and seasonal, temporary, and part-time W-2 employees. It brings onboarding, verification, work, billing, payments, tax compliance, and reporting together on one worker record. This plugin connects your AI assistant to Wingspan through our permissioned MCP server, so you can get Wingspan work done without switching apps. Invite contractors, review onboarding progress, identify missing requirements, and confirm whether a worker is ready to pay. Create payables, retrieve payment amounts, deductions, status, and history, or filter payments and calculate totals using the same criteria available in Wingspan. Human approval stays in control. Plugin requests approval before executing, creates new payables as drafts, and never moves money. Final approval and payment execution remain in Wingspan, with your existing permissions and audit trail preserved. Requires a Wingspan account with payer access.

Language: English · Automatically detected from descriptions.

Files & skills

File archives

Plugin package20 files · 25.1 KBBrowse files →
Skill instructions
checking-payments10.2 KB

View saved version →

---
name: checking-payments
description: Look up what a company owes its contractors through Wingspan and what has happened to a particular payment. Use for "what do we owe", "what did we pay last month", "how much is outstanding", "what is awaiting approval", "show me [name]'s payments", "what happened to this payment", "why has this not been paid yet", "has this been paid", "is this payment disputed".
---

# Checking payments

The shared rules for every call — ids, paging, previewing a write, what the
tools cannot do — are in the `using-wingspan-tools` skill. Apply them here.

A **payable** is one payment owed to one contractor — the row on the Payables
screen in the Wingspan app. A **contractor** is a person or business the company
pays. An **engagement** is the named working arrangement a payment is filed
under.

Two tools cover most of this: `search_payables` lists and filters, `get_payable`
reads one payment in full. Every row a search returns carries a `payableId`, and
that is the id `get_payable` takes. A third, `get_payroll_preview`, reads what
the next payroll run would pay as things stand today.

## Listing and searching

`search_payables` with no arguments lists every payment except cancelled ones,
25 to a page.

| Argument | What it does |
| --- | --- |
| `query` | Free-text search over the contractor's name, email and company, plus the invoice number. At least two characters; a full email address matches exactly. |
| `status` | Which screen view to read: `all`, `draft`, `toApprove`, `scheduled`, `paid`, `cancelled`. |
| `contractor` | One contractor's payments only — their `contractorId`, your external id for them, or their email. |
| `referenceId` | The one payment carrying your own id for it, set when it was created. No two payables share one. |
| `dueDateFrom`, `dueDateTo` | Bound the due date, as `YYYY-MM-DD`, inclusive. |
| `paidDateFrom`, `paidDateTo` | Bound the date paid, as `YYYY-MM-DD`, inclusive. |
| `sortBy` | `dueDate`, `createdAt`, `updatedAt`, `openedAt`, `paidAt`, `amount` or `scheduledPaymentDate`. One field per call. |
| `sortDirection` | `asc` or `desc`. Defaults to `desc`. Refused without `sortBy`. |
| `limit` | Rows per page, 1 to 100. Defaults to 25. |
| `pageToken` | Continue a previous page. Pass the previous result's `pagination.nextPageArgs` back unchanged. |
| `accountId` | Read one child account of an organization instead of the signed-in account. Only when the user names one; `who_am_i` lists them. |

There are no other filters. Do not invent one.

What each `status` view holds:

- `all` — every payment except cancelled ones.
- `draft` — created but not yet opened, so the contractor cannot see it.
- `toApprove` — open and waiting for someone at the company to approve it.
- `scheduled` — approved and waiting for a payroll run.
- `paid` — paid or in transit only. Payments recorded off-platform, partially
  paid or refunded are not in this view; use `all` for a complete history.
- `cancelled` — cancelled. Hidden everywhere else, which is why totals stay
  honest.

Every view, `all` included, also leaves out two kinds of row: the batch-level
total a payroll run creates, which would double-count against the individual
payments, and personal payment-link invoices. Neither can be read by id either.

## Totals

A list normally carries a `summary` with a count and an amount total, covering
**every** payment matching the filters, not only this page. Use it for "how
much do we owe" instead of adding up a page, and say which filters it covers.
When `summary` is null the totals were unavailable — say so rather than summing
the page and presenting it as the total.

A correct total over the wrong set of payments is still the wrong answer, so
pick the view before reading the summary:

- **"What do we owe?"** means approved-and-unpaid plus open-and-unapproved.
  Read `scheduled` (approved, waiting for a payroll run) and `toApprove` (open,
  waiting for approval) separately and report both figures with their names.
  Do not read `all`: it includes paid, in-transit, off-platform and refunded
  payments.
- **Drafts are not owed yet.** `draft` payments are invisible to the contractor
  and not scheduled. Report them as a separate line if the user asks what is
  in the pipeline, never inside the owed figure.
- **Partial payments are not separable here.** A partially paid payable shows
  its full amount in whichever view holds it; the summary has no
  remaining-balance figure. If the roster has partially paid payables, say the
  owed figure may overstate what remains, and point to the Wingspan app for
  the exact balance.
- **`paid` is history, not liability.** It answers "what have we paid", and
  only for payments paid or in transit; off-platform and refunded records sit
  under `all`.

## The words on a row

Each row's status is the wording the Wingspan app shows, not a raw code, so it
can be read to the user as-is.

| Row says | Means |
| --- | --- |
| Draft | Created and not yet opened. |
| Action required | Waiting on someone at the company: approval, a dispute the contractor raised, or something the contractor resubmitted. |
| Awaiting contractor | Open, but the contractor was not eligible for payment when it came up. |
| Scheduled | Approved and queued for a future payroll. |
| Paid | Paid, or the payment is in route. |
| Refunded | Refunded, in whole or in part. |
| Off-platform | A historical record of a payment made outside Wingspan. |
| Cancelled | Cancelled. |

One more value can appear on a row: `Unknown`, when the payment is in a state
the row wording has no pill for — a returned deposit is the common case. Read
`get_payable` for it; the detail headline names it, Returned. The money did not
land, the payment stays on the payroll run it was part of, and it is final for
that payment: a replacement is a new payment, created in the Wingspan app.

**Approval is a separate field from status.** A payment can be open and
unapproved, or open and approved; the status wording above folds that in, but
they are two different things underneath. Approving happens in the app.

## One payment in full

`get_payable` returns what the Payables detail panel shows:

- The panel headline and the row's status wording.
- Any alert on it. Two exist: the contractor has not finished setting up
  digital payments, and the contractor disputed the invoice — their reason is
  in the activity timeline.
- The amount, with the breakdown from gross to net and a named row per
  deduction.
- The line items, the due date, whether the due date was rescheduled, and the
  original date if it was.
- How it was paid, the attachments, any notes and purchase-order or project
  labels.
- `activity` — the full timeline of everything that has happened to it,
  newest first.

Quirks of the timeline are worth knowing before you conclude something did not
happen. At most two views of the invoice link are ever listed — the first, and
the first more than an hour after it — so a short timeline is not evidence the
contractor stopped looking. Views after the payment was paid are dropped. A
"due today" reminder sent on the same day the payment was opened is suppressed.

## What these tools cannot see

**Where the money is in the banking system.** No payout or bank-transfer
record is read, matching what the Payables screen itself shows. The furthest
either tool goes is the date the deposit was confirmed. A confirmed deposit
means the sending bank finished processing, not that the money is available or
final — a payment can still come back afterwards, and that shows up on the
payment itself, where `get_payable` names the state Returned. A question like
"has the bank transfer landed" or "why did the transfer fail" belongs in the
Wingspan app, or with Wingspan support.

**Past payroll runs and funding sources.** A payroll run is the batch that funds
and pays a set of approved payments, and the funding source is the account
Wingspan debits to fund it. `get_payroll_preview` reads the *next* run before it
goes out — when it processes, how much moves, what is funded but held on
eligibility, and what gets left behind. Runs that have already happened, and the
account behind any of them, are not readable here. When a user asks why a
scheduled batch has not gone out, or which account funded it, send them to the
Payroll screens in the app.

**What the preview is worth predicting with: nothing.** It is today's data, not
a forecast. It reports what the next run would pay if it went out against the
records as they stand right now, and it models none of what happens between now
and then — a contractor finishing a requirement and becoming eligible, someone
opening or approving a draft, an amount edited, a payment cancelled, a new
payable created. Any of those changes the answer. Report it as "as things stand
today" and never as what the run will pay.

## Why is this not paid yet

Work down this list.

1. `get_payable`. If the status is Draft, it was never opened. If it says
   action required, the secondary line on the row says which of the three it
   is — approval, a dispute or a resubmission. If it says awaiting contractor,
   the contractor was not eligible when the payment came up.
2. If the answer points at the contractor, `get_contractor` with the
   `contractorId` on the payment. Its `alert` gives the single reason —
   invited and not signed up, tax information not shared, archived, payments
   eligibility pending, an outstanding requirement, or one expired or
   expiring. The `finding-contractors` skill lists all eight outcomes.
3. If the payment is paid but the contractor says the money has not arrived,
   that is the banking question above: Wingspan app, or Wingspan support.

**"Awaiting contractor" is not a reason to cancel anything.** It means the
contractor was not eligible at the moment payment came up. Fix the contractor
and the payment carries on. Cancelling and recreating loses the record and,
because a payment's engagement is fixed when it is created, is sometimes
unrecoverable.

## Finish these in the Wingspan app

- Approving or unapproving a payment, rescheduling it, cancelling it, and
  paying it. Releasing a draft is not app work — that is `open_payables`.
- Starting a payroll run, funding sources, invoices, payment splits and
  accounting integrations.
- Resolving a dispute with a contractor.
- Anything about a bank transfer after Wingspan has sent the payment.

Referenced files: 1

connect1.81 KB

View saved version →

---
name: connect
description: Check the Wingspan connection and report who it is signed in as. Run this after installing the plugin, after re-authorizing, or whenever the user explicitly wants to confirm ChatGPT is reading the correct Wingspan account.
---

# Check the Wingspan connection

Call `who_am_i`. It takes no arguments and performs a real authenticated read,
so a successful answer proves the whole connection works end to end.

Then report, in plain sentences:

- **Connected as** — the person's email address, when the connection
  identifies an individual. No name is returned. An authorization granted
  through the browser identifies the company account rather than the
  individual, and the person comes back unavailable; say that it identifies the
  account, and do not present it as a problem.
- **Reading as** — the account from `account` when present, otherwise the
  matching entry in `accounts`. `actingAsAccountId` is always null here,
  because `who_am_i` takes no `accountId`; ignore it.
- **Accounts reachable** — the entries in `accounts`, by name, noting any that
  sit under a parent account. Mention that a tool can be pointed at one of them
  with its `accountId` if the user needs a different one. An empty `accounts`
  list is legitimate, not a fault.

Then say what the connection is for in one line: these tools read the
contractors this account pays, what it owes them, and their onboarding
paperwork, and they can create contractor records and draft payments after
showing a preview. Paying, approving and anything requiring an extra identity
challenge stay in the Wingspan app.

## If it fails

Follow the `troubleshooting` skill. In short: an authentication
failure means re-authorizing through `/mcp`; no Wingspan tools at all means the
server is not connected, and that skill says how to tell the two apart.

Referenced files: 1

creating-draft-payables10.3 KB

View saved version →

---
name: creating-draft-payables
description: Create draft payables — new payment obligations to contractors — in Wingspan. Nothing is paid. Use for "log a payment", "pay [name] $500 for [work]", "record these payments", "add these payments to the [engagement] engagement", "create payables", "log 12 hours at $85 for [name]", "bill this month's work". Writes to the company's Wingspan account, so it always previews first.
---

# Creating draft payables

The shared rules for every call — ids, paging, previewing a write, what the
tools cannot do — are in the `using-wingspan-tools` skill. Apply them here.

A **payable** is one payment owed to one contractor — the row on the Payables
screen in the Wingspan app. An **engagement** is the named working arrangement
a payment is filed under. A **line item** is one priced line inside a payment.

`create_payables` records payments against contractors' engagements. **It
writes to the company's account, and everything it creates is a draft.**

## Always preview first

1. Call `create_payables` with the rows and no `mode`. It defaults to
   `mode: "preview"`, which writes nothing: it resolves each contractor and
   engagement, checks every amount and date, totals it up, and reports exactly
   what applying would do.
2. Show that preview to the user in full — the per-row amounts, the total, the
   engagement each payment lands on, every warning, and every row that cannot
   be created.
3. Wait for the user to say yes. An earlier "log these payments" is not consent
   to write; the preview is the thing being consented to.
4. Call again with `mode: "apply"`, a `requestId` you have not used before,
   the matching preview's `confirmationToken` unchanged,
   and the **same rows and the same `ref` values** you previewed.

Retrying an apply with the same `requestId` and the same refs returns the
result of the original writes; nothing new is created. That is the only safe way
to retry. A genuinely new attempt gets a new `requestId`.

## Arguments

| Argument | What it does |
| --- | --- |
| `payments` | The rows. At least one, at most 50 per call. |
| `engagement` | An engagement, by name or id, for every row. Omit it and Wingspan uses each contractor's default engagement. A row's own `engagement` overrides it. |
| `dueDate` | Default due date for every row, as `YYYY-MM-DD`. Required unless every row sets its own. |
| `currency` | Currency for every row. Defaults to US dollars. |
| `mode` | `preview` (the default, writes nothing) or `apply`. |
| `confirmationToken` | Required with `apply`; copy it verbatim from the matching preview. If it expires or arguments change, preview again. |
| `requestId` | Required with `apply`. An id of your own, up to 64 printable characters with no spaces. |
| `accountId` | Write into one child account of an organization instead of the signed-in account. Only when the user names one; `who_am_i` lists them. |

Each row in `payments`:

| Field | What it does |
| --- | --- |
| `contractor` | **Required.** Their email, your external id for them, or their contractor id. |
| `ref` | Your label for this row, echoed in the result. Up to 48 printable characters, no spaces and no colon. Defaults to `row-1`, `row-2` and so on. |
| `referenceId` | Your own id for this payable, stored on it and searchable afterwards with `search_payables`' `referenceId` filter. One per payable — two cannot share one. |
| `engagement` | The engagement for this row, by name or id. Overrides the top-level one. |
| `amount` | A flat amount in dollars, for example `1200.50`. |
| `quantity` | Units worked, for example hours. Goes with `unitCost`. |
| `unitCost` | Amount per unit, in dollars. Goes with `quantity`. |
| `unit` | What a unit is: `"Hour"`, `"Unit"`, or a label of your own. Defaults to `"Unit"`. |
| `description` | What the work was. Shown as the line item's title. |
| `detail` | Longer detail underneath the line item. |
| `dueDate` | Due date for this row, as `YYYY-MM-DD`. Overrides the top-level one. |
| `notes` | A note on the payment itself. The contractor can see it. |
| `lineItems` | Several priced lines instead of the single-line fields above. |

Each entry in `lineItems` takes `description`, `amount`, `quantity`,
`unitCost`, `unit` and `detail`, with the same meanings.

**`ref` and `referenceId` are different things.** `ref` is a label for this
call: it comes back in the result, it is what the retry key is built from, and
it is gone once the call is done. `referenceId` is stored on the payable itself
and is how the user finds that payment again later. A row can carry both, and
they do not have to match. Because no two payables can share a `referenceId`,
reusing one is refused rather than attached to a second payment — which also
means a genuine retry of an apply is safe, but re-sending the same
`referenceId` under a *new* `requestId` is not.

## Amounts

**Amounts are in dollars.** `1200.50` is one thousand two hundred dollars and
fifty cents. Never send cents.

**Price a line one way or the other, never both.** Either a flat `amount`, or
`quantity` together with `unitCost` — twelve hours at eighty-five dollars is
`quantity: 12, unitCost: 85, unit: "Hour"`. Sending both is refused rather than
guessed at, because it means the amount was expressed twice. A rate-priced line
needs both halves: `quantity` on its own, or `unitCost` on its own, is refused.

**Use the single-line fields or `lineItems`, never both on one row.** Same
reason.

**Every amount has to be greater than zero.** A flat `amount` of zero, a
`unitCost` of zero and a `quantity` of zero are each refused. A payment for
nothing is never what the user meant.

**A flat `amount` cannot be more precise than the currency.** US dollars are
paid to two decimal places, so `10.999` is refused. Round the figure with the
user rather than picking one for them. A per-unit `unitCost` may be finer than
that — half a cent across a thousand units is a real way to price work — and
Wingspan does the multiplication.

**A due date is required**, either on every row or once at the top level, and
it is a calendar date: `YYYY-MM-DD`, no time and no timezone.

## Engagements

The engagement decides which working arrangement the payment belongs to, and
**it is fixed the moment the payment is created.** There is no way to move a
payment to a different engagement afterwards — not from here, and not in the
app. Getting it wrong means cancelling the payment and creating a new one. So
when the engagement matters, confirm it with the user before applying.

The contractor must already be assigned to the engagement you name. If they are
not, the row fails and the preview lists which engagements they *are* assigned
to. Assigning them is app work; the `onboarding-contractors` skill covers doing
it for a new contractor.

Omit the engagement entirely and Wingspan files the payment under the
contractor's default engagement. The preview says when that is happening, so
show it — a user who cares which engagement a payment lands on needs to see
that they did not name one.

**This tool creates new payment obligations. It never moves existing ones.**
A payable's engagement is fixed when it is created, and no tool here edits a
payable. So "add these payments to an engagement" is ambiguous: if the user
means payments that already exist in Wingspan, that cannot be done from here,
and previewing a creation would propose duplicate obligations. Before calling
the tool, settle which one the user means. If they mean existing records,
check with `search_payables` and say the engagement cannot be changed. If they
mean new drafts, proceed. When the phrasing is "log", "record" or "add" a
payment that has already been paid outside Wingspan, ask as well: a draft
payable is a new obligation that Wingspan will expect to pay, not a record of
money already sent.

## Everything created here is a draft

A payment created by this tool sits at draft. The contractor cannot see it and
no payment is scheduled. Say this to the user every time, because "log a
payment" often means "and pay it" in their head.

What happens next: `open_payables` releases the draft, which is what shows it to
the contractor, and that is the one step available here. Approving it, and the
payroll run that funds and pays it, happen in the Wingspan app. Paying is also
protected by an extra identity challenge, so it cannot be reached from here
under any circumstances.

## The warning that matters most

**Eligibility is checked when a payment is opened, not when it is created.** A
draft against a contractor with outstanding requirements is created happily
and may then fail to open, if any of those requirements blocks eligibility.
Whether a given outstanding requirement blocks depends on where it was
attached, which these tools do not read, so say "may not open" rather than
"cannot open". The preview flags the situation — "onboarding requirements are
incomplete, so this payable cannot be opened or paid until they are" — and
that warning must reach the user, not be dropped as noise. Use
`get_contractor` to say what is outstanding; the `finding-contractors` skill
covers reading it.

The preview also warns when a contractor's assignment to the named engagement
is not active yet.

## Batches

Fifty rows is the hard limit for one call; over that, the tool refuses and
names the limit. Each batch is its own attempt and needs its own `requestId`.
One bad row never stops the others — each row reports its own outcome by `ref`.

## Where payments come from besides this tool

An invoice is not a payable. A payable is the payer's record of what it owes one
contractor. An invoice is a bill, and either side can raise one: a contractor
billing the company, or the company billing its own client. Payables can
originate from either kind of invoice as well as from payroll, and all of them
show up in `search_payables` alongside anything created here — the
`checking-payments` skill covers reading them. A payable that came from an
invoice is owned by Wingspan and cannot be edited here.

## Finish these in the Wingspan app

- Approving a draft, scheduling it, cancelling it and paying it. Releasing it
  so the contractor can see it is `open_payables`, not app work.
- Starting a payroll run, and funding sources.
- Moving a payment to a different engagement — impossible; cancel and recreate.
- Invoices, payment splits, deductions and accounting integrations.
- Creating engagements, and assigning a contractor to one.

Referenced files: 1

finding-contractors12.7 KB

View saved version →

---
name: finding-contractors
description: Find, filter and inspect the contractors a company pays through Wingspan, including who still has onboarding paperwork outstanding. Use for "who do we pay", "list our contractors", "find [name]", "who has not signed up yet", "who is missing their W-9", "who is blocked by the certificate of insurance", "which contractors are blocked from being paid", "can we pay [name]", "what is [name]'s status", "who is on this engagement".
---

# Finding contractors

The shared rules for every call — ids, paging, previewing a write, what the
tools cannot do — are in the `using-wingspan-tools` skill. Apply them here.

A **contractor** is a person or business the company pays; Wingspan also calls
this a *payee*. An **engagement** is a named working arrangement a contractor
is assigned to. A **requirement** is something a contractor must satisfy before
they can be paid, such as a tax form or an insurance certificate.

Two tools cover this: `search_contractors` lists and filters, and
`get_contractor` reads one contractor in full. Every row a search returns
carries a `contractorId`, and that is the id `get_contractor` takes.

## Listing and searching

`search_contractors` with no arguments lists active contractors, newest first,
25 to a page.

| Argument | What it does |
| --- | --- |
| `query` | Free-text search over name, email, company and your own external id. At least two characters. Results come back by relevance. |
| `status` | Which screen tab to read: `active`, `notSignedUp`, `incomplete`, `complete`, `archived`. |
| `onboarding` | Where the working relationship stands: `Pending`, `Active`, `Inactive`. |
| `eligibility` | `blocked` or `clear`, as reported by the search API. Missing eligibility is neither; use the verification workflow below before concluding nobody is blocked. |
| `engagement` | Only contractors assigned to this engagement, by name or id. |
| `batch` | Only contractors loaded by one bulk import in the Wingspan app, by that import's batch id. |
| `requirement` | Only contractors whose copy of this requirement is in `requirementState`. |
| `requirementState` | Which state that requirement is in. Defaults to `incomplete`. |
| `sortBy` | `createdAt` or `updatedAt`. One field per call. |
| `sortDirection` | `asc` or `desc`. Defaults to `desc`. Refused without `sortBy`. |
| `limit` | Rows per page, 1 to 100. Defaults to 25. |
| `pageToken` | Continue a previous page. Pass the previous result's `pagination.nextPageArgs` back unchanged. |
| `accountId` | Read one child account of an organization instead of the signed-in account. Only when the user names one; `who_am_i` lists them. |

Use the current tool schema for supported filters. Do not invent filters or assume this table is exhaustive.

## Two separate questions, two separate arguments

`status` is the tab strip from the Wingspan app, and it mixes the working
relationship with paperwork: `incomplete` and `complete` are a summary of
requirements, while `notSignedUp` and `archived` are about the relationship.

`onboarding` is the relationship on its own, and every row reports it whatever
you filtered on.

The two combine, so `status: "notSignedUp"` with `onboarding: "Pending"` means
"invited, has not signed up". One pair is refused rather than answered:
`onboarding: "Inactive"` together with `status: "active"`, because `active`
excludes archived contractors and `Inactive` requires them. `status` defaults
to `active`, so leaving it out is the same pair — send `onboarding: "Inactive"`
with `status: "archived"`.

One more thing worth passing on: `onboarding: "Inactive"` matches archived
contractors, but a contractor who only rejected their invite is not matched.
The underlying search cannot express "archived or rejected" in one query, and
the result's `note` says so.

**One kind of contractor no filter finds:** someone archived before any
engagement was assigned to them. Every `status` tab except `active` needs a
paperwork or engagement summary they never got, and `active` excludes anyone
archived — so no combination of `status` and `onboarding` returns them. Read one
with `get_contractor` if you have their `contractorId`; its headline reads
Archived. Every row that does come back carries its own `archived` field,
whatever you filtered on.

## Who is blocked from being paid

Use `search_contractors` with `eligibility: "blocked"` as a candidate search,
not proof that every other contractor is eligible. Preserve the user's account
and roster scope. Do not add an `onboarding: "Active"` filter: a contractor
still onboarding can have a non-eligible engagement.

If the blocked search is empty, fails, or conflicts with the user's observation,
read the roster without the eligibility filter. Also verify individual records
whenever roster rows have `paymentEligibility: null`, empty `engagements`, or
missing requirement summaries. These values mean unknown in search, not clear.
Search has been observed returning no engagements while detail returns actual
assignments; do not recommend assigning an engagement based only on search.

Call `get_contractor` using each candidate or unknown row's `contractorId`.
For a small roster, inspect every returned row when checking a negative result.
For larger rosters, respect paging and the user's requested scope; explicitly
state how many records were checked and whether further pages or unknowns
remain. Never turn a partial check or failed detail call into "nobody is blocked".

For payment eligibility, inspect `engagements[].paymentsEligibility` and the
engagement status. Report `NotEligible` as ineligible for that engagement;
include a `Created` engagement instead of discarding it for not being
`Activated`. An eligible engagement does not cancel out another non-eligible
engagement. Distinguish historical inactive assignments from current work.
Report `Eligible` at the engagement level, not as a guarantee that funds can
be delivered. Missing or unrecognized eligibility remains unknown.

Explain outstanding requirements using `requirementDetails`. Match a
requirement's `engagementIds` to the assignment's `payeeEngagementId`, not the
template `engagementId`. Report the returned statuses, such as
`PendingCompletion`. Separate confirmed engagement ineligibility from the
outstanding requirements: without placement-specific evidence, do not claim
each outstanding requirement independently blocks payment.

Use `alert` for the general onboarding headline, but do not let it override
explicit engagement eligibility. If it says invited or not signed up while
the Sign up requirement is completed, disclose that inconsistency rather
than choosing one as a proven explanation. When search and detail disagree,
report the detail's explicit engagement verdict and explain that the search
missed it. Indexing or mapping issues are hypotheses until independently
confirmed.

Only report no ineligible engagements found after checking the relevant scope;
include any unresolved records and coverage limits. Do not use absence from
the blocked search as evidence that everyone can be paid.

## Who still has a named requirement outstanding

Two calls, in this order.

1. `search_requirements` lists the requirements the company has configured.
   Each carries a `requirementDefinitionId`, the `blocking` value Wingspan
   holds for it, its grace period and how often it expires. Pick the one the
   user named. With no arguments it lists the ones still in use; it also takes
   `type` (one kind of requirement), `includeInactive` (show retired ones too),
   `limit`, `pageToken` and `accountId`, and nothing else.
2. `search_contractors` with `requirement` set to that id; its exact name also
   works, matched case-insensitively and in full, so a partial name will not
   resolve. Set `requirementState` to the state you want.

| `requirementState` | Means |
| --- | --- |
| `incomplete` | Outstanding — the contractor has not finished it. This is the default. |
| `pendingReview` | The contractor submitted something and the company has not reviewed it. |
| `complete` | Satisfied, whether the contractor or the company finished it. |
| `expiring` | Satisfied, but the expiry date is approaching. |
| `expired` | It lapsed and has to be renewed. |

A requirement the company rejected, or one whose underlying record was revoked
or failed, is reopened rather than failed — it reads as outstanding again, with
a reason recorded. There is no failed state. Say "reopened after review" rather
than "never started" when reporting one.

`requirementState` on its own is refused: it needs `requirement`.

Two things to say out loud when reporting the answer:

- **Not every outstanding requirement blocks a payment.** `blocking` on a
  definition is what Wingspan reports for that definition. A contractor's own
  copy can be blocking or not depending on where it was attached.
  `get_contractor` treats every incomplete requirement as blocking and does
  not read that setting; which placement merely tracks a requirement is visible
  only in the Wingspan app.
- **`gracePeriodDays` is a delay on the definition**, not a verdict about a
  person: it is the days from when a requirement is attached to a contractor
  before an outstanding copy starts holding up a payment. Inside that window an
  unfinished requirement does not block.

When interpreting requirement kinds or filtering by `type`, read
[references/requirement-types.md](references/requirement-types.md).

## When to read one contractor in full

Move to `get_contractor` once the question is about one person: "can we pay
them", "what is their status", "what is missing". It returns what the
Contractors detail screen shows. For general onboarding status, lead with
`alert`. For payment eligibility, use the engagement verification workflow
above. The headline can have one of these outcomes:

| `alert` says | Means |
| --- | --- |
| Contractor invited | Invited, has not signed up yet. |
| Tax information not shared | Signed up, but has not shared tax information with the company. |
| Archived | Someone at the company archived them. |
| Contractor is all set for now | Ready to be paid. |
| Payments eligibility pending | Signed up with nothing outstanding, but at least one active assignment is not cleared for payment. |
| Requirements expired | Something already satisfied has lapsed and needs renewing. |
| Requirements expiring soon | Something already satisfied is about to lapse. |
| Requirements incomplete | Something is outstanding. |

Only one applies, and the order above decides which. A contractor who is both
archived and missing tax information reads "Tax information not shared",
because that check comes first. This headline is not a substitute for the
explicit `paymentsEligibility` on each engagement.

"Tax information not shared" does not always mean the contractor must act — a
payer can supply and verify the details itself, in the Wingspan app.

`alert` can also be empty, when none of the checks above fire. Report that as
"nothing flagged" and fall back to the assignments and requirement list in the
same result rather than declaring the contractor payable.

`get_contractor` also returns their tax information and its verification
status, every engagement assignment with a count of outstanding requirements,
the full requirement list with expiry dates, whether they have a payout method
set up (somewhere for the money to land), and your own id for them.

## Two results to read carefully

**Missing search summaries do not prove there are no assignments.**
When search reports no engagements or paperwork summary, verify with
`get_contractor` before explaining the cause or recommending changes. A genuine
absence of assignments is different from missing indexed data.

**An assignment with no requirements shows "None required".** It also shows
whatever eligibility Wingspan reports. Do not infer from it. If such a
contractor cannot be paid, the fix is to attach one requirement in the Wingspan
app and have it completed; nothing else re-evaluates eligibility.

## A search that finds nobody

If a name search returns no rows and archived contractors were excluded, the
result reports how many archived contractors do match. Offer to look there
before telling the user the person does not exist.

## Finish these in the Wingspan app

- Creating or editing engagements, groups, worksites, custom fields and rate
  cards.
- Writing a requirement definition, and attaching one to an engagement or a
  group.
- Approving, rejecting, resetting or renewing one contractor's requirement, and
  extending an expiry date.
- Archiving or restoring a contractor.
- Everything the contractor does themselves: signing up, signing, uploading a
  document, verifying identity, adding a payout method.
- Sharing tax information, which is usually the contractor's step; a company
  that records and verifies a contractor's taxpayer details itself also does
  that in the app.

Referenced files: 2

onboarding-contractors6.9 KB

View saved version →

---
name: onboarding-contractors
description: Add contractors to Wingspan, assign them to an existing engagement and send their invites. Use for "add these contractors", "onboard these people", "invite [name] to Wingspan", "create contractor records", "set up these five contractors", "invite this roster", "add them to the [engagement] engagement". Writes to the company's Wingspan account, so it always previews first.
---

# Onboarding contractors

The shared rules for every call — ids, paging, previewing a write, what the
tools cannot do — are in the `using-wingspan-tools` skill. Apply them here.

A **contractor** is a person or business the company pays; Wingspan also calls
this a *payee*. An **engagement** is a named working arrangement a contractor is
assigned to. An **invite** is the email that lets the contractor claim their own
Wingspan account.

`create_contractors` does three things in one call: it creates each contractor
record, assigns each one to an existing engagement, and emails the invite to
everyone who has not come on board yet. **It writes to the company's account.**

## Always preview first

1. Call `create_contractors` with the rows and no `mode`. It defaults to
   `mode: "preview"`, which writes nothing: it looks up the engagement, checks
   every email address, and reports exactly what applying would do — including
   which rows already exist.
2. Show that preview to the user in full: how many would be created, how many
   would be skipped, which rows have problems, which engagement each one would
   be assigned to, and how many invite emails would be sent and to how many
   people.
3. Wait for the user to say yes. Do not treat an earlier "add these people" as
   consent to write; the preview is the thing being consented to.
4. Call again with `mode: "apply"`, a `requestId` you have not used before,
   the matching preview's `confirmationToken` unchanged,
   and the **same rows and the same `ref` values** you previewed.

## Arguments

| Argument | What it does |
| --- | --- |
| `contractors` | The rows. At least one, at most 50 per call. |
| `engagement` | An existing engagement, by name or id, to assign every row to. A row's own `engagements` overrides it. |
| `sendInvites` | Email the invite to everyone not yet on board. Defaults to true. |
| `mode` | `preview` (the default, writes nothing) or `apply`. |
| `confirmationToken` | Required with `apply`; copy it verbatim from the matching preview. If it expires or arguments change, preview again. |
| `requestId` | Required with `apply`. An id of your own, up to 64 printable characters with no spaces. |
| `accountId` | Write into one child account of an organization instead of the signed-in account. Only when the user names one; `who_am_i` lists them. |

Each row in `contractors`:

| Field | What it does |
| --- | --- |
| `email` | **Required.** The invite goes here, and it identifies the contractor. |
| `ref` | Your label for this row, echoed in the result. Up to 48 printable characters, no spaces and no colon. Defaults to `row-1`, `row-2` and so on. |
| `name` | Full name. Split into first and last name at the first space, exactly as the Wingspan app does. |
| `company` | Business name, if they invoice as a company. |
| `externalId` | Your own id for this contractor, for reconciliation. |
| `phone` | Contact phone number. |
| `engagements` | Engagements for this row specifically, by name or id. Overrides the top-level `engagement`. |

There are no other fields. Do not invent one — custom fields and group
membership are app work.

## What `requestId` and `ref` are for

Every write carries a key built from your `requestId` and the row's `ref`. If an
apply half-succeeds and you call again with the *same* `requestId` and the same
refs, you get back the result of the original writes; nothing new is created.
That is the only safe way to retry. Change the `requestId` only when
starting a genuinely new attempt, and never renumber refs between the preview
and the apply — refs, not row order, are what the keys are built from.

Results come back by `ref`. Email addresses and names are deliberately absent
from the result, including from error messages, so keep your own mapping from
ref to person if the user needs one.

## What each row can come back as

- **created** — the contractor record was created, and the invite was sent
  unless `sendInvites` was false.
- **skipped** — the contractor already existed. Nothing was duplicated. If
  they existed but had never come on board, the invite still went out to them:
  that is what makes a retry of a half-finished batch safe.
- **failed** — that row alone failed, with a reason and often the field at
  fault. One bad row never stops the others.

A row can also report engagement problems separately from the contractor
itself: the record was created but an assignment did not stick.

## The invite, and what happens next

The invite creates a pending claim record and emails a one-time link to the
address on the row. Wingspan decides which person that address belongs to; a
caller never supplies one.

From there:

- **Pending** — waiting for the recipient.
- **Linked** — they accepted, and the contractor record is now bound to the
  Wingspan account they chose. This is permanent.
- **Rejected** — they declined. Inviting them again creates a fresh, separate
  claim, and that is done in the Wingspan app.

`search_contractors` reports this as `onboarding`, with `Pending`, `Active` and
`Inactive`. The `finding-contractors` skill covers reading it.

## Engagements

Assign contractors to an **existing** engagement. This tool never creates one:
if the user names an engagement the company does not have, the preview says so,
and creating it is app work.

A contractor created with no engagement is a real record, but it cannot be paid
until an engagement is assigned. Say that when a user asks for bare records.

## Batches

Fifty rows is the hard limit for one call; over that, the tool refuses and
names the limit rather than quietly dropping rows. Batches of roughly 25 are
easier for a person to read in a preview. Each batch is its own attempt and
needs its own `requestId`.

This is a synchronous call, not a bulk importer. A roster of several hundred
people belongs in the Wingspan app's import screen.

## Finish these in the Wingspan app

- Creating or editing engagements, worksites, groups, custom fields and rate
  cards.
- Setting a contractor's custom-field values, adding them to a group, or
  setting their rate.
- Re-sending, retargeting or cancelling an invite, and inviting again after a
  rejection.
- Attaching requirements, and approving or rejecting what a contractor
  submits.
- Everything the contractor does themselves: accepting the invite, signing up,
  signing documents, uploading certificates, verifying identity, adding a payout
  method.
- Sharing tax information, which is usually the contractor's step; a company
  that records and verifies a contractor's taxpayer details itself also does
  that in the app.

Referenced files: 1

outstanding4.1 KB

View saved version →

---
name: outstanding
description: List the contractors who have one named onboarding requirement outstanding, such as a W-9, a certificate of insurance or a background check. Run it with the requirement's name. It reports who has not finished the requirement; it does not confirm whose payments are blocked by it.
---

# Who still has this outstanding: $ARGUMENTS

A **requirement** is something a contractor must satisfy before the company can
pay them. The company configures each one once, and every contractor placed on
an engagement gets their own copy. This answers "who has not finished
$ARGUMENTS". It does not answer "whose payment is blocked": whether an
outstanding copy blocks payment depends on where it was attached, which these
tools do not read.

If no requirement was named, list the company's requirements with
`search_requirements` and ask which one the user means. Do not guess.

## Step 1 — find the requirement

Call `search_requirements`. Match `$ARGUMENTS` against the `name` of each
result, case-insensitively and in full. A partial name does not count as a
match. The result is one page; if nothing on it matches and the result carries
`pagination.nextPageArgs`, call again with those arguments and keep going until
a match appears or there is no next page. Only then is "no match" true.

- **No match on any page.** Show the names that came back and ask which one
  was meant. Do not substitute a similar-sounding one.
- **More than one match.** Show the candidates and ask. Requirement names can
  be near-identical across engagements, and answering for the wrong one is
  worse than asking.

Keep two fields from the match: its `requirementDefinitionId` and its
`blocking` value.

## Step 2 — find who has not finished it

Call `search_contractors` with `requirement` set to that
`requirementDefinitionId` and `requirementState` set to `incomplete`, which
means outstanding — the contractor has not finished it.

That returns one page. Report the page, then offer the next one using the
result's `pagination.nextPageArgs`; do not fetch further pages unasked.

## Step 3 — report

Lead with the count and the requirement's full name, worded as "have this
outstanding", not "are blocked". Then list the contractors by name, with their
onboarding state, so the user can see who has not even signed up yet versus
who signed up and has not finished.

Always add this caveat, whatever the definition's `blocking` value says:
whether an outstanding copy actually holds up a payment depends on where the
requirement was attached to that contractor. A placement that is not blocking
only tracks the copy; an engagement placement can also block eligibility while
still allowing payment. `get_contractor` does not read that setting and treats
every incomplete requirement as blocking, so the Wingspan app is the place to
confirm before telling anyone their payment is or is not blocked. If the
definition is not marked blocking, say so as well: an outstanding copy is then
usually tracked rather than holding anyone up.

Then add whichever of these applies:
- **If the user wants a different slice**, the same pair of calls answers it
  with a different `requirementState`: `pendingReview` for submitted and not
  yet reviewed, `expiring` for satisfied but about to lapse, `expired` for
  lapsed, `complete` for finished.
- **A rejected or revoked requirement reads as outstanding again.** One the
  company rejected, or one whose underlying record was revoked or failed, is
  reopened rather than failed, with a reason recorded. There is no failed
  state, so say "reopened after review" rather than "never started" about such
  a contractor.

For one contractor's full picture, `get_contractor` with their `contractorId`.

## What cannot be done from here

Nudging a contractor, approving or rejecting what they submitted, resetting or
renewing a requirement, and extending an expiry date all happen in the Wingspan
app. So does everything the contractor does themselves — signing, uploading,
verifying identity. Sharing tax information is usually the contractor's step
too, but a company can instead record and verify a contractor's taxpayer
details itself, in the app.

Referenced files: 1

troubleshooting5.31 KB

View saved version →

---
name: troubleshooting
description: Diagnose Wingspan connection and permission problems. Use when a Wingspan tool returns an authentication or permission error, when tools report nothing, when an answer appears to come from the wrong company or account, or when someone asks whether ChatGPT is connected to Wingspan.
---

# Troubleshooting the Wingspan connection

The Wingspan tools read one company's records, over a connection the user
authorized in their browser. When something looks wrong, the question is
almost always which account the tools are reading as, or whether the
authorization is still good.

## Run `who_am_i` first

`who_am_i` takes no arguments and is the only honest check that the connection
works, because it performs a real authenticated read. What it reports:

- The signed-in person's email address, and no name, when the connection
  identifies a person. An authorization granted through the browser identifies
  the company account rather than the individual, and in that case the person is
  reported as unavailable — that is expected, not a fault.
- `actingAsAccountId` — always null from `who_am_i`, because it takes no
  `accountId`; ignore it. The account the answers are about is `account`, which
  the browser authorization names, or the entry in `accounts` matching the
  signed-in person. Other tools read as a child account only when a call passes
  `accountId`.
- `accounts` — the accounts this connection can reach, with names and any
  parent account. An empty list is legitimate, not a fault.

Report what it says before theorising.

## Not authorized, or authorization expired

An authentication failure means the connection needs re-authorizing. Open the
connector or MCP settings, select Wingspan, and complete login in the browser.

Two things worth knowing so you can describe this accurately:

- Access is granted for a short window and then renewed automatically. This
  plugin asks for the permission that makes silent renewal possible, so a
  working connection normally stays working without anyone doing anything.
- If the renewal itself fails — the authorization was revoked, or the browser
  session is long gone — every tool starts failing at once. Re-authorizing
  through `/mcp` is the fix, not retrying the tool.

If a tool reports that the token was rejected, the message carries a request id
when Wingspan supplied one. Give that id to the user; it is the handle Wingspan
support needs to find the failure.

## The answers are for the wrong company

Two causes, and `who_am_i` distinguishes them.

**The connection is bound to a different account than the user expected.**
`account` names the account the authorization covers when the report carries
one; otherwise it is the entry in `accounts` matching the signed-in person.
`accounts` shows what else is reachable. A person who works with more than one
Wingspan account has to authorize the one they mean.

**The company is an organization with child accounts.** Nine of the ten
tools take an optional `accountId` that acts as one child account instead of the
default; `who_am_i` takes no arguments at all. Use `accountId` only when the user
names a specific child account, and take the id from `who_am_i`'s `accounts`
list. Do not guess an id, and do not sweep across children unasked.

## The answers are empty

Before concluding there is no data:

- `search_contractors` defaults to active contractors and excludes archived
  ones. A name search that finds nothing reports how many archived contractors
  do match; offer to look there.
- `search_payables` excludes cancelled payments unless asked for them.
- A contractor with no engagement assignment has no paperwork summary at all,
  so the tabs that filter on paperwork will not return them.
- These tools answer only for the company doing the paying. If the user wants
  to know what someone owes *them*, these tools cannot answer it, and their own
  Wingspan account is where to look.

The `finding-contractors` and `checking-payments` skills cover each of those in
more detail.

## What the error messages mean

| The tool says | What happened |
| --- | --- |
| Not found | No such record is visible to this account. A record belonging to a different account also reads as not found, on purpose. |
| Not a well-formed id | The id is the wrong shape. Use the id a search result carried, not one copied from elsewhere. |
| Invalid request | The arguments contradict each other. The message says which pair; fix it and call again. |
| The token was rejected | The authorization is no longer accepted. Re-authorize through `/mcp`. |

## When the tools are not there at all

If no Wingspan tools appear, the plugin may be installed without an active
connection. Check the connector or MCP settings for the Wingspan entry and its
status. If the endpoint cannot be found, ask the user's Wingspan account team
whether the MCP integration is enabled for the account.

## Finish these in the Wingspan app

- Signing in, resetting a password, and anything to do with multi-factor
  authentication.
- Reviewing or revoking which applications have access to the account.
- Creating, rotating or deleting an API key, or managing a service account.
- Changing which accounts a person can reach, and their permissions.

Every one of those is protected by an extra identity challenge, which cannot be
completed in a conversation. There is no workaround to look for.

Referenced files: 1

using-wingspan-tools8.97 KB

View saved version →

---
name: using-wingspan-tools
description: >-
  The shared rules every Wingspan tool call follows: ids, paging, previewing a write, and what the tools cannot do. Load this before any Wingspan tool call, and whenever someone asks about the people they pay through Wingspan, what they owe, onboarding paperwork, invites, invoices or payments — for example "who do we pay", "what do we owe this month", "is this contractor ready to be paid", "add these contractors", "log a payment", "why has this not been paid".
---

# Using the Wingspan tools

Wingspan is a payments platform. A company that pays people uses it to bring
those people on board, collect their tax and compliance paperwork, and pay
them. This plugin gives ChatGPT read access to that company's own records, plus
three carefully limited write actions.

## Words used throughout

- **Payer** — the company doing the paying. Everything here is written from
  the payer's side.
- **Contractor** (called a *payee* inside Wingspan) — a person or business the
  payer pays.
- **Engagement** — a named working arrangement a contractor is assigned to,
  such as "Q3 copywriting" or "Design retainer". Payments are filed under an
  engagement, and requirements can be attached to one.
- **Requirement** — something a contractor must satisfy before the payer can
  pay them: a tax form, a signature, an insurance certificate, a background
  check. A requirement the payer configured is a *definition*; one contractor's
  copy of it is an *instance*.
- **Payable** — one payment owed to one contractor. In the Wingspan app this is
  a row on the Payables screen.

These tools answer only for the company doing the paying. If you want to know
what someone owes you, they cannot answer it.

For unfamiliar terminology, read [references/glossary.md](references/glossary.md).

## The ten tools

| Tool | What it answers | Reads or writes |
| --- | --- | --- |
| `who_am_i` | Does the connection work, and which account is it reading? | Reads |
| `search_contractors` | Who do we pay? Who matches this name? Who is held up? | Reads |
| `get_contractor` | Can we pay this one contractor, and if not, why? | Reads |
| `search_requirements` | What must a contractor satisfy before we can pay them? | Reads |
| `search_payables` | What do we owe, and what have we paid? | Reads |
| `get_payable` | What happened to this one payment? | Reads |
| `get_payroll_preview` | What would the next payroll run pay, as things stand today? | Reads |
| `create_contractors` | Create contractors, assign an engagement, send invites. | **Writes** |
| `create_payables` | Log payments as drafts. | **Writes** |
| `open_payables` | Release drafts so contractors can see them. | **Writes** |

In a tool list these appear as `mcp__codex_apps__wingspan_mcp2_who_am_i` and so
on. Reason about the short names above.

Use the available Wingspan-MCP2 tool schemas for exact arguments; tool prefixes can vary by host. If a schema differs from a field list below, follow the current schema.

## Rules for every call

**Use the id the search gave you.** Each row from `search_contractors` carries
a `contractorId`, and each row from `search_payables` carries a `payableId`.
Those are the ids `get_contractor` and `get_payable` accept. Ids copied from
anywhere else — a spreadsheet, a URL, another system — will usually be
rejected.

**One page per call.** `search_contractors` and `search_payables` return a
single page, then hand back `pagination.nextPageArgs`: the complete set of
arguments for the next call. Show the page to the user and offer to fetch the
next one. Do not loop through pages unasked. A page token only works with the
exact same filters and sort that produced it, so pass `nextPageArgs` back
unchanged.

**Read the `note`.** Every list result carries a plain-sentence `note` saying
how many rows came back, how the list was sorted, and what to do next. It also
warns about things that are easy to misread, such as a filter that cannot
return what the user expects. Pass that on rather than dropping it.

**Preview, show, confirm, then apply.** All three write tools default to
`mode: "preview"`, which changes nothing: it looks up every id, checks every
row, and reports exactly what applying would do. Always preview first, show
that preview to the user in full, and wait for them to say yes. Only then call
again with `mode: "apply"` and a `requestId` you have not used before. Never
apply on your own initiative, and never apply without having previewed the same
rows.

**Pass the preview token.** Every apply requires the matching preview's `confirmationToken`, passed back verbatim with the same previewed arguments. Never construct a token. A missing, altered or expired token requires a new preview. For explicit `open_payables` rows, also return each preview's `ifMatch` fingerprint unchanged; if the selection or records changed, preview again before applying.

**Reuse `ref`, and pick a fresh `requestId` per attempt.** Each row in a
`create_contractors` or `create_payables` call has a `ref`, your own label for
that row. Results come back by `ref`, and the safety mechanism that stops a
retry from creating duplicates is built from `ref` plus `requestId` — so send
the *same* refs on apply that you sent on preview. Retrying an apply with the
same `requestId` and the same refs returns the result of the original writes;
nothing new is created. A genuinely new attempt gets a new `requestId`.

`open_payables` works the same way but has no `ref`: its rows are payments that
already exist, so it reports each one by `payableId`, and that id is what its
retry key is built from. Listing the same `payableId` twice in one call is
refused.

**Amounts are in dollars.** `1200.50` means one thousand two hundred dollars
and fifty cents. Never send cents.

**Payments are created as drafts.** `create_payables` stops at a draft the
contractor cannot see, with no payment scheduled. `open_payables` releases it,
which is what shows it to the contractor. Approving it and paying it happen in
the Wingspan app.

**Some things are deliberately out of reach.** Any action Wingspan protects with
an extra identity challenge — paying a payable, paying an invoice, moving money
between accounts, creating or rotating an API key — cannot be done from here at
all, because there is nowhere in this conversation to complete that challenge.
Say so plainly and point the user at the Wingspan app rather than looking for a
workaround.

**Child accounts.** Nine of the ten tools take an optional `accountId`,
which acts as one child account of an organization instead of the signed-in
account. `who_am_i` takes no arguments at all. Only use `accountId` when the
user names a specific child account; `who_am_i` lists the ones reachable.

## Do not confuse these

- **An invoice is not a payable.** A payable is the payer's record of what it
  owes one contractor. An invoice is a bill, and either side can raise one: a
  contractor billing the company, or the company billing its own client.
  Payables can originate from either kind of invoice as well as from payroll,
  and all of them show up in `search_payables` — the individual payments from a
  payroll run appear there, the payroll batch total itself does not. A payable
  that came from an invoice is owned by Wingspan and cannot be edited here.
- **A requirement definition is not one contractor's progress.**
  `search_requirements` lists the payer's templates. One contractor's progress
  against them comes from `get_contractor`.
- **A relationship id is not an account id.** A `contractorId` identifies the
  payer's record of that contractor, not the Wingspan account the contractor
  signed in to. Only `accountId` takes an account id.

## Finish these in the Wingspan app

The tools cannot do any of the following, and no combination of them adds up to
it. Tell the user which screen to go to instead.

- Creating or editing engagements, worksites, groups, custom fields, rate cards
  or requirement definitions.
- Attaching a requirement to an engagement or a group, and approving,
  rejecting, resetting or renewing one contractor's requirement.
- Approving, scheduling, cancelling or paying a payable; funding sources;
  starting a payroll run; invoices; payment splits; accounting integrations.
  Releasing a draft is the one step here that is not app work — that is
  `open_payables`.
- Re-sending, retargeting or cancelling an invite.
- Everything the contractor does themselves: signing up, signing a document,
  uploading a certificate, verifying their identity, adding a payout method.
- Sharing tax information, which is usually the contractor's step; a company
  that records and verifies a contractor's taxpayer details itself also does
  that in the app.

## Where to go next

- Finding and filtering contractors, and who is held up:
  the `finding-contractors` skill.
- What is owed, and what happened to one payment: the `checking-payments` skill.
- Adding contractors: the `onboarding-contractors` skill.
- Creating draft payables: the `creating-draft-payables` skill.
- Connection problems and wrong-account answers: the
  `troubleshooting` skill.

Referenced files: 2

Package details

Publisher declarations from the archived package. These are separate from our research and the live service's terms.

Package author
Wingspan

Package observed Oct 2, 2026.

Technical details
First seen
Sep 30, 2026 · 22:02 UTC
Last seen
Oct 2, 2026 · 12:00 UTC
Collection status
Collected

plugin_asdk_app_6aa7b09b97808191b0ced38534cd8782

Download plugin data (JSON)