Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ npx skills add apideck/api-skills --skill apideck-php
# REST API (any language)
npx skills add apideck/api-skills --skill apideck-rest

# MCP server (for agent-driven integrations: Claude Code, Cursor, OpenAI Agents SDK, Pydantic AI)
npx skills add apideck/api-skills --skill apideck-mcp

# Best practices (recommended with any SDK skill)
npx skills add apideck/api-skills --skill apideck-best-practices

Expand Down Expand Up @@ -80,7 +83,7 @@ Every skill in the catalog lives under `skills/` — the canonical path scanned

The `connectors/` directory holds the tooling that generates connector skills (`manifest.json`, `generate.js`, `validate.js`, `_enhancements/`) — no SKILL.md output lives there.

### `apideck-*` skills (13)
### `apideck-*` skills (18)

Apideck-specific API abstractions, SDK wrappers, and tooling. Install individually or as a set. **`apideck-unified-api` is the front door** — install it first.

Expand All @@ -99,6 +102,11 @@ Apideck-specific API abstractions, SDK wrappers, and tooling. Install individual
| [apideck-go](skills/apideck-go/) | SDK | Unified API integration patterns for Go | `github.com/apideck-libraries/sdk-go` |
| [apideck-php](skills/apideck-php/) | SDK | Unified API integration patterns for PHP | `apideck-libraries/sdk-php` |
| [apideck-rest](skills/apideck-rest/) | SDK | Direct REST patterns for any language — raw HTTP, authentication headers, response handling | — |
| [apideck-mcp](skills/apideck-mcp/) | MCP | Front-door for the Apideck MCP server — 330 unified-API tools + 4 intent-grouped workflow tools, dynamic discovery, Vault OAuth elicitations | [`@apideck/mcp`](https://github.com/apideck-libraries/mcp) |
| [apideck-mcp-pay-bill](skills/apideck-mcp-pay-bill/) | MCP playbook | Task playbook for `apideck-pay-bill` — pay a known vendor bill (AP) | — |
| [apideck-mcp-receive-payment](skills/apideck-mcp-receive-payment/) | MCP playbook | Task playbook for `apideck-receive-customer-payment` — record a customer payment against an invoice (AR) | — |
| [apideck-mcp-onboard-employee](skills/apideck-mcp-onboard-employee/) | MCP playbook | Task playbook for `apideck-onboard-employee` — convert a hired ATS applicant into an HRIS employee (cross-API) | — |
| [apideck-mcp-month-end-close](skills/apideck-mcp-month-end-close/) | MCP playbook | Task playbook for `apideck-month-end-close-check` — fetch P&L + balance sheet + aged AP/AR in one shot | — |

### Connector Skills

Expand Down
117 changes: 117 additions & 0 deletions skills/apideck-mcp-month-end-close/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
---
name: apideck-mcp-month-end-close
description: Task playbook for fetching a month-end financial snapshot via the Apideck MCP server's `apideck-month-end-close-check` workflow tool. Use when the user asks for a P&L, balance sheet, aged creditors, or aged debtors view at a point in time. Read-only, idempotent, returns partial snapshots when some reports aren't supported by the connector.
license: Apache-2.0
alwaysApply: false
metadata:
author: apideck
version: "1.0.0"
---

# Month-end close snapshot (Apideck MCP)

When the user wants a one-shot financial snapshot, **prefer `apideck-month-end-close-check`** over fetching the four reports individually. The workflow fans out aged creditors, aged debtors, balance sheet, and profit-and-loss in parallel against the connected accounting service and returns one aggregated object.

## When this is the right tool

| User intent | Tool |
|---|---|
| "Run the month-end close", "P&L plus balance sheet for last month", "Aged AP/AR snapshot as of March 31" | **`apideck-month-end-close-check`** ✓ |
| "Just the P&L" | `accounting-profit-and-loss-get` directly |
| "Just aged creditors" | `accounting-aged-creditors-get` directly |
| "Reconcile bank statements" | Out of scope — use the connector's native reconciliation |

## IMPORTANT RULES

- **READ-ONLY, idempotent.** Safe to call repeatedly — no confirmation needed. The tool fetches data; it doesn't post anything.
- **OMIT `report_as_of_date` to use today.** Most users running a month-end ask for "last month", in which case pass an explicit `YYYY-MM-DD` like `"2026-03-31"`.
- **EXPECT partial results.** Connector coverage varies — Odoo doesn't implement aged-creditors, Moneybird doesn't implement balance-sheet, etc. The workflow returns `{ unsupported: true, reason }` for steps the connector can't fulfill, plus a top-level `warnings[]` array. Surface these to the user instead of treating them as failures.
- **`isError: true` only fires when *every* report failed** — typically a missing connection or expired credentials. Partial snapshots come back as `isError: false` so the agent can still extract value.

## Argument map

| Arg | Required | Default | Notes |
|---|---|---|---|
| `report_as_of_date` | no | today (YYYY-MM-DD) | Cutoff date for aged reports + balance sheet. |
| `x-apideck-service-id` | no | first accounting connection | E.g. `"xero"`, `"quickbooks"`. |

## Result shape

### Full success

```json
{
"report_as_of_date": "2026-03-31",
"service_id": "xero",
"aged_creditors": { "summary": [...] },
"aged_debtors": { "summary": [...] },
"balance_sheet": { "assets": 100000, ... },
"profit_and_loss":{ "revenue": 250000, ... }
}
```

### Partial — some reports unsupported

```json
{
"report_as_of_date": "2026-03-31",
"service_id": "odoo",
"aged_creditors": { "unsupported": true, "reason": "Aged-creditors not implemented for Odoo" },
"aged_debtors": { "unsupported": true, "reason": "..." },
"balance_sheet": { "assets": 100000, ... },
"profit_and_loss":{ "revenue": 250000, ... },
"warnings": [
"aged_creditors: unsupported on odoo (...)",
"aged_debtors: unsupported on odoo (...)"
]
}
```

`isError: false` — two reports came back, that's still useful.

### Total failure

```json
{
"report_as_of_date": "2026-03-31",
"service_id": null,
"aged_creditors": { "error": "..." },
"aged_debtors": { "error": "..." },
"balance_sheet": { "error": "..." },
"profit_and_loss":{ "error": "..." },
"warnings": [...]
}
```

With `isError: true`. Usually a missing connection — surface the elicitation URL.

## Worked example

User: *"Give me a month-end snapshot for March 2026 from QuickBooks."*

```json
{
"name": "apideck-month-end-close-check",
"arguments": {
"report_as_of_date": "2026-03-31",
"x-apideck-service-id": "quickbooks"
}
}
```

Then summarize for the user, calling out any `unsupported` rows distinctly:

> *On 2026-03-31 in QuickBooks: revenue $250K, total assets $100K. Aged creditors / aged debtors aren't supported by this connector — pull those from the Bills-list and Invoices-list reports if you need them.*

## Common failure modes

| Symptom | Cause | Fix |
|---|---|---|
| All four reports `unsupported` | Connector doesn't implement these (e.g. Odoo without the Reports module) | Surface the limitation; suggest aggregating from `accounting-bills-list` + `accounting-invoices-list` instead |
| `UnsupportedFiltersError` on a report | Connector rejects `filter[report_as_of_date]` or `filter[end_date]` | Connector quirk — currently surfaces as `unsupported` with the upstream message |
| `UrlElicitationRequiredError` | Connection expired/missing | Surface consent URL, retry |

## Related

- [`apideck-mcp`](../apideck-mcp/) — front-door skill
- Workflow source: [src/gen/workflows/monthEndClose.ts](https://github.com/apideck-libraries/mcp/blob/main/src/gen/workflows/monthEndClose.ts)
1 change: 1 addition & 0 deletions skills/apideck-mcp-month-end-close/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"version":"1.0.0","organization":"Apideck","date":"April 2026","abstract":"Task playbook for fetching a month-end financial snapshot via the Apideck MCP server's apideck-month-end-close-check workflow tool.","references":["https://github.com/apideck-libraries/mcp/blob/main/src/gen/workflows/monthEndClose.ts","https://mcp.apideck.dev/mcp"]}
132 changes: 132 additions & 0 deletions skills/apideck-mcp-onboard-employee/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
---
name: apideck-mcp-onboard-employee
description: Task playbook for converting a hired ATS applicant into an HRIS employee via the Apideck MCP server's `apideck-onboard-employee` workflow tool. First cross-unified-API workflow — requires both ATS and HRIS connections active on the consumer. Optionally moves the applicant to a "hired" stage in the ATS to close the loop.
license: Apache-2.0
alwaysApply: false
metadata:
author: apideck
version: "1.0.0"
---

# Onboard an employee from an applicant (Apideck MCP)

When the user has decided to hire a candidate and wants the new employee record created in their HRIS, **prefer `apideck-onboard-employee`** over manually mapping ATS fields onto an HRIS create call. The workflow fetches the applicant, maps name/contact/address fields onto the HRIS shape, creates the employee, and optionally updates the ATS stage.

## When this is the right tool

| User intent | Tool |
|---|---|
| "Onboard candidate Alice from Greenhouse to BambooHR", "Convert applicant 42 into an employee starting Monday" | **`apideck-onboard-employee`** ✓ |
| "Create an employee from scratch (no ATS lineage)" | `hris-employees-create` directly |
| "Move applicant to a different ATS stage without HRIS work" | `ats-applicants-update` directly |
| "Re-hire an existing employee" | Out of scope — usually a different HRIS endpoint per connector |

## IMPORTANT RULES

- **CONFIRM before calling.** Onboard is **mutating and not idempotent** — calling twice creates two HRIS employee records. Always confirm with the user the applicant name, start date, department, and target HRIS.
- **CROSSES TWO UNIFIED APIS.** The consumer needs both an ATS connection (Greenhouse, Lever, Workable, …) AND an HRIS connection (BambooHR, Workday, Personio, …) authorized in Vault. If either is missing the workflow throws an elicitation pointing at the missing one.
- **TWO SEPARATE SERVICE-ID HEADERS.** Unlike the accounting workflows, this one takes `x-apideck-ats-service-id` *and* `x-apideck-hris-service-id` separately — the underlying connectors are different, and the routing has to be set per call.
- **`hired_stage_id` IS OPTIONAL AND SOFT-FAILS.** If you pass it, the workflow tries to move the applicant to that stage in the ATS *after* creating the employee. If the ATS update fails, the workflow returns `isError: false` with a `warnings[]` entry — the employee was already created, and rolling back would leave the workspace in a worse state than partial success. Surface the warning to the user so they can move the applicant manually.
- **VALIDATION HAPPENS AT THE BOUNDARY.** If the applicant has no first or last name, the workflow returns `failingStep: "validate-applicant"` before touching the HRIS — the connector would reject a blank-name employee anyway, and we'd rather fail fast.

## Argument map

| Arg | Required | Default | Notes |
|---|---|---|---|
| `applicant_id` | yes | — | From `ats-applicants-list`. |
| `employment_start_date` | yes | — | First day of employment, `YYYY-MM-DD`. Most HRIS connectors require this. |
| `department_id` | no | — | From `hris-departments-list`. Some HRIS connectors require it; others derive from job. |
| `title` | no | applicant's `headline` or `title` | Job title. |
| `manager_id` | no | — | Reporting manager's HRIS employee id. |
| `employment_status` | no | `"active"` | One of `active`, `inactive`, `pending`, `leave`, `terminated`. |
| `hired_stage_id` | no | — | If set, moves the ATS applicant to this stage after employee creation. Soft-fails. |
| `x-apideck-ats-service-id` | no | first ATS connection | E.g. `"greenhouse"`, `"lever"`. |
| `x-apideck-hris-service-id` | no | first HRIS connection | E.g. `"bamboohr"`, `"workday"`. |

## Result shape

### Success (no stage move)

```json
{
"applicant_id": "app-1",
"employee_id": "emp-99",
"first_name": "Ada",
"last_name": "Lovelace",
"employment_start_date": "2026-05-01",
"title": "Senior Engineer",
"department_id": "dept-eng",
"ats_service_id": "greenhouse",
"hris_service_id": "bamboohr"
}
```

### Soft-fail (employee created, stage move failed)

```json
{
"applicant_id": "app-1",
"employee_id": "emp-99",
"first_name": "Ada",
"last_name": "Lovelace",
...
"warnings": [
"Employee created but ATS stage update failed: Greenhouse momentarily unavailable. Move the applicant to the Hired stage manually."
]
}
```

`isError: false`. Tell the user the employee landed in HRIS, and they need to mark the applicant Hired manually.

### Hard fail

```json
{
"applicant_id": "app-X",
"error": "...",
"failingStep": "ats-applicants-get" | "validate-applicant" | "hris-employees-create",
"upstream": { ... }
}
```

`failingStep` values:
- `ats-applicants-get` — applicant ID wrong, ATS connection missing, or ATS connector down. Likely an elicitation if connection-level.
- `validate-applicant` — applicant has no first/last name; can't create a usable employee.
- `hris-employees-create` — HRIS rejected the body. Inspect `upstream` for the per-connector reason (often `department_id required` or `manager_id invalid`).

## Worked example

User: *"Onboard applicant app-1 (Ada Lovelace) into BambooHR starting May 1, engineering department, manager mgr-7. Move them to the Hired stage in Greenhouse."*

1. **Confirm**: *"Creating BambooHR employee Ada Lovelace, start 2026-05-01, department dept-eng, manager mgr-7. Will also move the Greenhouse applicant to stage `stage-hired`. Confirm?"*
2. On confirmation:
```json
{
"name": "apideck-onboard-employee",
"arguments": {
"applicant_id": "app-1",
"employment_start_date": "2026-05-01",
"department_id": "dept-eng",
"manager_id": "mgr-7",
"hired_stage_id": "stage-hired",
"x-apideck-ats-service-id": "greenhouse",
"x-apideck-hris-service-id": "bamboohr"
}
}
```
3. Surface `employee_id` to the user. If `warnings[]` is present, tell them what didn't happen.

## Common failure modes

| Symptom | Cause | Fix |
|---|---|---|
| `failingStep: validate-applicant` | Applicant record has empty name fields | Verify applicant ID; consider whether the ATS exposes the name under a different field name |
| `failingStep: hris-employees-create` with `department_id required` | Connector requires explicit department | Pass `department_id` from `hris-departments-list` |
| `warnings[]` with stage-update failure | Optional ATS update failed after employee was created | Tell user to mark applicant Hired manually; don't retry the workflow (would create a duplicate employee) |
| `UrlElicitationRequiredError` for ATS or HRIS | Either connection missing | Surface consent URL for the named unified API, retry |

## Related

- [`apideck-mcp`](../apideck-mcp/) — front-door skill
- Workflow source: [src/gen/workflows/onboardEmployee.ts](https://github.com/apideck-libraries/mcp/blob/main/src/gen/workflows/onboardEmployee.ts)
- ATS connectors: [`ats`](../ats/), HRIS connectors: [`hris`](../hris/) — for understanding which downstream services support what fields
1 change: 1 addition & 0 deletions skills/apideck-mcp-onboard-employee/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"version":"1.0.0","organization":"Apideck","date":"April 2026","abstract":"Task playbook for converting a hired ATS applicant into an HRIS employee via the Apideck MCP server's apideck-onboard-employee workflow tool.","references":["https://github.com/apideck-libraries/mcp/blob/main/src/gen/workflows/onboardEmployee.ts","https://mcp.apideck.dev/mcp"]}
Loading
Loading