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
Skill instructions
checking-payments10.2 KB
--- 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
--- 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
--- 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
--- 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
--- 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
--- 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
--- 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
--- 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)