jpskill.com
💼 ビジネス コミュニティ

xero-accounting

Xero会計システムと連携し、請求書や経費、銀行取引などのデータを同期したり、損益計算書や貸借対照表といった財務諸表を自動で作成したりすることで、会計業務を効率化するSkill。

📜 元の英語説明(参考)

Integrate with the Xero accounting API to sync invoices, expenses, bank transactions, and contacts — and generate financial reports like P&L and balance sheet. Use when: connecting apps to Xero, automating bookkeeping workflows, syncing accounting data, or pulling financial reports programmatically.

🇯🇵 日本人クリエイター向け解説

一言でいうと

Xero会計システムと連携し、請求書や経費、銀行取引などのデータを同期したり、損益計算書や貸借対照表といった財務諸表を自動で作成したりすることで、会計業務を効率化するSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o xero-accounting.zip https://jpskill.com/download/15578.zip && unzip -o xero-accounting.zip && rm xero-accounting.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/15578.zip -OutFile "$d\xero-accounting.zip"; Expand-Archive "$d\xero-accounting.zip" -DestinationPath $d -Force; ri "$d\xero-accounting.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して xero-accounting.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → xero-accounting フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

Xero Accounting

概要

Xero API を介して、アプリケーションを Xero の会計プラットフォームと統合します。この Skill では、OAuth2 認証、主要な会計オブジェクト (請求書、請求書、経費、連絡先、銀行取引) の同期、および財務レポート (損益計算書、貸借対照表、試算表) の取得について説明します。ワンタイムインポートと継続的な同期パターンの両方をサポートします。

手順

ステップ 1: OAuth2 認証の設定

Xero は PKCE を使用した OAuth2 を使用します。developer.xero.com でアプリを登録し、トークンフローを実装します。

# xero_auth.py — Xero API の OAuth2 トークン管理
import requests
import base64
import json
import os
from datetime import datetime, timedelta

XERO_CLIENT_ID = os.environ["XERO_CLIENT_ID"]
XERO_CLIENT_SECRET = os.environ["XERO_CLIENT_SECRET"]
XERO_REDIRECT_URI = os.environ["XERO_REDIRECT_URI"]
TOKEN_FILE = ".xero_tokens.json"

def get_auth_url():
    """OAuth2 認証 URL を生成します。"""
    import secrets
    state = secrets.token_urlsafe(16)
    params = {
        "response_type": "code",
        "client_id": XERO_CLIENT_ID,
        "redirect_uri": XERO_REDIRECT_URI,
        "scope": "openid profile email accounting.transactions accounting.reports.read accounting.contacts offline_access",
        "state": state,
    }
    query = "&".join(f"{k}={v}" for k, v in params.items())
    return f"https://login.xero.com/identity/connect/authorize?{query}"

def exchange_code_for_tokens(code: str) -> dict:
    """認証コードをアクセス + リフレッシュトークンと交換します。"""
    credentials = base64.b64encode(
        f"{XERO_CLIENT_ID}:{XERO_CLIENT_SECRET}".encode()
    ).decode()

    response = requests.post(
        "https://identity.xero.com/connect/token",
        headers={
            "Authorization": f"Basic {credentials}",
            "Content-Type": "application/x-www-form-urlencoded",
        },
        data={
            "grant_type": "authorization_code",
            "code": code,
            "redirect_uri": XERO_REDIRECT_URI,
        },
    )
    tokens = response.json()
    tokens["expires_at"] = (
        datetime.utcnow() + timedelta(seconds=tokens["expires_in"])
    ).isoformat()
    save_tokens(tokens)
    return tokens

def refresh_access_token(tokens: dict) -> dict:
    """リフレッシュトークンを使用してアクセストークンを更新します。"""
    credentials = base64.b64encode(
        f"{XERO_CLIENT_ID}:{XERO_CLIENT_SECRET}".encode()
    ).decode()

    response = requests.post(
        "https://identity.xero.com/connect/token",
        headers={
            "Authorization": f"Basic {credentials}",
            "Content-Type": "application/x-www-form-urlencoded",
        },
        data={
            "grant_type": "refresh_token",
            "refresh_token": tokens["refresh_token"],
        },
    )
    new_tokens = response.json()
    new_tokens["expires_at"] = (
        datetime.utcnow() + timedelta(seconds=new_tokens["expires_in"])
    ).isoformat()
    save_tokens(new_tokens)
    return new_tokens

def get_valid_token() -> str:
    """有効なアクセストークンを返します。必要に応じて更新します。"""
    tokens = load_tokens()
    expires_at = datetime.fromisoformat(tokens["expires_at"])
    if datetime.utcnow() >= expires_at - timedelta(minutes=5):
        tokens = refresh_access_token(tokens)
    return tokens["access_token"]

def get_tenant_id(access_token: str) -> str:
    """Xero 組織 (テナント) ID を取得します。"""
    response = requests.get(
        "https://api.xero.com/connections",
        headers={"Authorization": f"Bearer {access_token}"},
    )
    connections = response.json()
    return connections[0]["tenantId"]  # 最初に接続された組織を使用

def save_tokens(tokens: dict):
    with open(TOKEN_FILE, "w") as f:
        json.dump(tokens, f)

def load_tokens() -> dict:
    with open(TOKEN_FILE) as f:
        return json.load(f)

ステップ 2: API クライアントの作成

# xero_client.py — 再利用可能な Xero API クライアント
import requests
from xero_auth import get_valid_token, get_tenant_id

class XeroClient:
    BASE_URL = "https://api.xero.com/api.xro/2.0"

    def __init__(self):
        self.token = get_valid_token()
        self.tenant_id = get_tenant_id(self.token)

    def _headers(self):
        return {
            "Authorization": f"Bearer {self.token}",
            "Xero-Tenant-Id": self.tenant_id,
            "Accept": "application/json",
            "Content-Type": "application/json",
        }

    def get(self, endpoint: str, params: dict = None) -> dict:
        response = requests.get(
            f"{self.BASE_URL}/{endpoint}",
            headers=self._headers(),
            params=params,
        )
        response.raise_for_status()
        return response.json()

    def post(self, endpoint: str, data: dict) -> dict:
        response = requests.post(
            f"{self.BASE_URL}/{endpoint}",
            headers=self._headers(),
            json=data,
        )
        response.raise_for_status()
        return response.json()

    def put(self, endpoint: str, data: dict) -> dict:
        response = requests.put(
            f"{self.BASE_URL}/{endpoint}",
            headers=self._headers(),
            json=data,
        )
        response.raise_for_status()
        return response.json()

ステップ 3: 請求書の同期


# sync_invoices.py — Xero での請求書の作成と取得
from xero_client import XeroClient
from datetime import datetime

client = XeroClient()

def create_invoice(contact_name: str, line_items: list, due_date: str, currency: str = "USD") -> dict:
    """
    Xero で売上請求書 (ACCREC) を作成します。

    line_items の形式:
      [{"description": "...", "quantity": 1, "unitAmount": 100.0, "accountCode": "200"}]
    """
    payload = {
        "Invoices": [{
            "Type": "ACCREC",
            "Contact": {"Name": contact_name},
            "DueDate": due_date,          # 例: "2025-03-31"
            "CurrencyCode": currency,
            "LineItems": [
                {


(原文がここで切り詰められています)
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Xero Accounting

Overview

Integrate your application with Xero's accounting platform via the Xero API. This skill covers OAuth2 authentication, syncing core accounting objects (invoices, bills, expenses, contacts, bank transactions), and pulling financial reports (P&L, balance sheet, trial balance). Supports both one-time imports and continuous sync patterns.

Instructions

Step 1: Set up OAuth2 authentication

Xero uses OAuth2 with PKCE. Register your app at developer.xero.com, then implement the token flow:

# xero_auth.py — OAuth2 token management for Xero API
import requests
import base64
import json
import os
from datetime import datetime, timedelta

XERO_CLIENT_ID = os.environ["XERO_CLIENT_ID"]
XERO_CLIENT_SECRET = os.environ["XERO_CLIENT_SECRET"]
XERO_REDIRECT_URI = os.environ["XERO_REDIRECT_URI"]
TOKEN_FILE = ".xero_tokens.json"

def get_auth_url():
    """Generate the OAuth2 authorization URL."""
    import secrets
    state = secrets.token_urlsafe(16)
    params = {
        "response_type": "code",
        "client_id": XERO_CLIENT_ID,
        "redirect_uri": XERO_REDIRECT_URI,
        "scope": "openid profile email accounting.transactions accounting.reports.read accounting.contacts offline_access",
        "state": state,
    }
    query = "&".join(f"{k}={v}" for k, v in params.items())
    return f"https://login.xero.com/identity/connect/authorize?{query}"

def exchange_code_for_tokens(code: str) -> dict:
    """Exchange authorization code for access + refresh tokens."""
    credentials = base64.b64encode(
        f"{XERO_CLIENT_ID}:{XERO_CLIENT_SECRET}".encode()
    ).decode()

    response = requests.post(
        "https://identity.xero.com/connect/token",
        headers={
            "Authorization": f"Basic {credentials}",
            "Content-Type": "application/x-www-form-urlencoded",
        },
        data={
            "grant_type": "authorization_code",
            "code": code,
            "redirect_uri": XERO_REDIRECT_URI,
        },
    )
    tokens = response.json()
    tokens["expires_at"] = (
        datetime.utcnow() + timedelta(seconds=tokens["expires_in"])
    ).isoformat()
    save_tokens(tokens)
    return tokens

def refresh_access_token(tokens: dict) -> dict:
    """Refresh the access token using the refresh token."""
    credentials = base64.b64encode(
        f"{XERO_CLIENT_ID}:{XERO_CLIENT_SECRET}".encode()
    ).decode()

    response = requests.post(
        "https://identity.xero.com/connect/token",
        headers={
            "Authorization": f"Basic {credentials}",
            "Content-Type": "application/x-www-form-urlencoded",
        },
        data={
            "grant_type": "refresh_token",
            "refresh_token": tokens["refresh_token"],
        },
    )
    new_tokens = response.json()
    new_tokens["expires_at"] = (
        datetime.utcnow() + timedelta(seconds=new_tokens["expires_in"])
    ).isoformat()
    save_tokens(new_tokens)
    return new_tokens

def get_valid_token() -> str:
    """Return a valid access token, refreshing if needed."""
    tokens = load_tokens()
    expires_at = datetime.fromisoformat(tokens["expires_at"])
    if datetime.utcnow() >= expires_at - timedelta(minutes=5):
        tokens = refresh_access_token(tokens)
    return tokens["access_token"]

def get_tenant_id(access_token: str) -> str:
    """Get the Xero organisation (tenant) ID."""
    response = requests.get(
        "https://api.xero.com/connections",
        headers={"Authorization": f"Bearer {access_token}"},
    )
    connections = response.json()
    return connections[0]["tenantId"]  # Use first connected org

def save_tokens(tokens: dict):
    with open(TOKEN_FILE, "w") as f:
        json.dump(tokens, f)

def load_tokens() -> dict:
    with open(TOKEN_FILE) as f:
        return json.load(f)

Step 2: Create an API client

# xero_client.py — Reusable Xero API client
import requests
from xero_auth import get_valid_token, get_tenant_id

class XeroClient:
    BASE_URL = "https://api.xero.com/api.xro/2.0"

    def __init__(self):
        self.token = get_valid_token()
        self.tenant_id = get_tenant_id(self.token)

    def _headers(self):
        return {
            "Authorization": f"Bearer {self.token}",
            "Xero-Tenant-Id": self.tenant_id,
            "Accept": "application/json",
            "Content-Type": "application/json",
        }

    def get(self, endpoint: str, params: dict = None) -> dict:
        response = requests.get(
            f"{self.BASE_URL}/{endpoint}",
            headers=self._headers(),
            params=params,
        )
        response.raise_for_status()
        return response.json()

    def post(self, endpoint: str, data: dict) -> dict:
        response = requests.post(
            f"{self.BASE_URL}/{endpoint}",
            headers=self._headers(),
            json=data,
        )
        response.raise_for_status()
        return response.json()

    def put(self, endpoint: str, data: dict) -> dict:
        response = requests.put(
            f"{self.BASE_URL}/{endpoint}",
            headers=self._headers(),
            json=data,
        )
        response.raise_for_status()
        return response.json()

Step 3: Sync invoices

# sync_invoices.py — Create and retrieve invoices in Xero
from xero_client import XeroClient
from datetime import datetime

client = XeroClient()

def create_invoice(contact_name: str, line_items: list, due_date: str, currency: str = "USD") -> dict:
    """
    Create a sales invoice (ACCREC) in Xero.

    line_items format:
      [{"description": "...", "quantity": 1, "unitAmount": 100.0, "accountCode": "200"}]
    """
    payload = {
        "Invoices": [{
            "Type": "ACCREC",
            "Contact": {"Name": contact_name},
            "DueDate": due_date,          # e.g. "2025-03-31"
            "CurrencyCode": currency,
            "LineItems": [
                {
                    "Description": item["description"],
                    "Quantity": item["quantity"],
                    "UnitAmount": item["unitAmount"],
                    "AccountCode": item.get("accountCode", "200"),
                }
                for item in line_items
            ],
            "Status": "AUTHORISED",
        }]
    }
    result = client.post("Invoices", payload)
    invoice = result["Invoices"][0]
    print(f"Created invoice {invoice['InvoiceNumber']} — Total: {invoice['Total']}")
    return invoice

def list_invoices(status: str = "AUTHORISED", modified_since: str = None) -> list:
    """Retrieve invoices, optionally filtered by status or modified date."""
    params = {"Status": status}
    headers_extra = {}
    if modified_since:
        headers_extra["If-Modified-Since"] = modified_since

    result = client.get("Invoices", params=params)
    return result.get("Invoices", [])

def mark_invoice_paid(invoice_id: str, amount: float, account_code: str = "090") -> dict:
    """Record a payment against an invoice."""
    payload = {
        "Payments": [{
            "Invoice": {"InvoiceID": invoice_id},
            "Account": {"Code": account_code},
            "Amount": amount,
            "Date": datetime.utcnow().strftime("%Y-%m-%d"),
        }]
    }
    result = client.post("Payments", payload)
    return result["Payments"][0]

Step 4: Sync bank transactions

# sync_bank_transactions.py — Push bank transactions into Xero
from xero_client import XeroClient

client = XeroClient()

def create_bank_transaction(
    account_id: str,
    contact_name: str,
    amount: float,
    date: str,
    description: str,
    account_code: str = "400",
    tx_type: str = "SPEND",  # SPEND or RECEIVE
) -> dict:
    """Record a bank transaction (spend or receive money)."""
    payload = {
        "BankTransactions": [{
            "Type": tx_type,
            "Contact": {"Name": contact_name},
            "BankAccount": {"AccountID": account_id},
            "Date": date,
            "LineItems": [{
                "Description": description,
                "Quantity": 1,
                "UnitAmount": abs(amount),
                "AccountCode": account_code,
            }],
            "IsReconciled": False,
        }]
    }
    result = client.post("BankTransactions", payload)
    return result["BankTransactions"][0]

def bulk_import_transactions(account_id: str, transactions: list) -> list:
    """
    Import multiple bank transactions in a single API call.

    transactions: list of dicts with keys:
      date, contact, amount (negative=spend, positive=receive), description, account_code
    """
    bank_txs = []
    for tx in transactions:
        tx_type = "SPEND" if tx["amount"] < 0 else "RECEIVE"
        bank_txs.append({
            "Type": tx_type,
            "Contact": {"Name": tx["contact"]},
            "BankAccount": {"AccountID": account_id},
            "Date": tx["date"],
            "LineItems": [{
                "Description": tx["description"],
                "Quantity": 1,
                "UnitAmount": abs(tx["amount"]),
                "AccountCode": tx.get("account_code", "400"),
            }],
        })

    payload = {"BankTransactions": bank_txs}
    result = client.post("BankTransactions", payload)
    created = result["BankTransactions"]
    print(f"Imported {len(created)} bank transactions")
    return created

Step 5: Generate financial reports

# reports.py — Pull P&L and balance sheet from Xero
from xero_client import XeroClient

client = XeroClient()

def get_profit_and_loss(from_date: str, to_date: str) -> dict:
    """
    Fetch Profit & Loss report.
    Dates format: YYYY-MM-DD
    """
    params = {
        "fromDate": from_date,
        "toDate": to_date,
    }
    result = client.get("Reports/ProfitAndLoss", params=params)
    report = result["Reports"][0]

    # Parse rows into a flat dict
    summary = {}
    for row in report.get("Rows", []):
        if row.get("RowType") == "SummaryRow":
            cells = row.get("Cells", [])
            if len(cells) >= 2:
                summary[cells[0].get("Value", "")] = cells[1].get("Value", "")

    return {"raw": report, "summary": summary}

def get_balance_sheet(date: str) -> dict:
    """Fetch Balance Sheet as of a given date (YYYY-MM-DD)."""
    result = client.get("Reports/BalanceSheet", params={"date": date})
    return result["Reports"][0]

def get_trial_balance(date: str) -> list:
    """Fetch Trial Balance as of a given date."""
    result = client.get("Reports/TrialBalance", params={"date": date})
    report = result["Reports"][0]
    rows = []
    for row in report.get("Rows", []):
        if row.get("RowType") == "Row":
            cells = row.get("Cells", [])
            if cells:
                rows.append({
                    "account": cells[0].get("Value"),
                    "debit": cells[1].get("Value") if len(cells) > 1 else None,
                    "credit": cells[2].get("Value") if len(cells) > 2 else None,
                    "ytd_debit": cells[3].get("Value") if len(cells) > 3 else None,
                    "ytd_credit": cells[4].get("Value") if len(cells) > 4 else None,
                })
    return rows

Step 6: Manage contacts

# contacts.py — Create and update Xero contacts (customers and suppliers)
from xero_client import XeroClient

client = XeroClient()

def upsert_contact(name: str, email: str = None, phone: str = None,
                   is_customer: bool = True, is_supplier: bool = False) -> dict:
    """Create or update a contact in Xero."""
    contact = {
        "Name": name,
        "IsCustomer": is_customer,
        "IsSupplier": is_supplier,
    }
    if email:
        contact["EmailAddress"] = email
    if phone:
        contact["Phones"] = [{"PhoneType": "DEFAULT", "PhoneNumber": phone}]

    payload = {"Contacts": [contact]}
    result = client.post("Contacts", payload)
    return result["Contacts"][0]

def find_contact_by_email(email: str) -> dict | None:
    """Look up a contact by email address."""
    result = client.get("Contacts", params={"EmailAddress": email})
    contacts = result.get("Contacts", [])
    return contacts[0] if contacts else None

Examples

Example 1: Sync Stripe payment as Xero invoice

# When a Stripe payment succeeds, create a matching Xero invoice and mark it paid
stripe_payment = {
    "customer_email": "client@example.com",
    "amount": 2500.00,
    "currency": "USD",
    "description": "Monthly SaaS subscription — March 2025",
    "date": "2025-03-01",
}

contact = upsert_contact(
    name=stripe_payment["customer_email"],
    email=stripe_payment["customer_email"],
    is_customer=True,
)

invoice = create_invoice(
    contact_name=contact["Name"],
    line_items=[{
        "description": stripe_payment["description"],
        "quantity": 1,
        "unitAmount": stripe_payment["amount"],
        "accountCode": "200",
    }],
    due_date=stripe_payment["date"],
    currency=stripe_payment["currency"].upper(),
)

mark_invoice_paid(invoice["InvoiceID"], amount=stripe_payment["amount"])
print(f"Synced invoice {invoice['InvoiceNumber']} to Xero ✓")

Example 2: Generate monthly P&L and print summary

pnl = get_profit_and_loss("2025-03-01", "2025-03-31")
print("March 2025 P&L Summary")
print("=" * 30)
for label, value in pnl["summary"].items():
    print(f"  {label:<20} {value:>12}")

Output:

March 2025 P&L Summary
==============================
  Total Income          $18,500.00
  Total Cost of Sales    $3,200.00
  Gross Profit          $15,300.00
  Total Expenses         $6,750.00
  Net Profit             $8,550.00

Environment Variables

Variable Description
XERO_CLIENT_ID OAuth2 client ID from Xero developer portal
XERO_CLIENT_SECRET OAuth2 client secret
XERO_REDIRECT_URI Callback URL registered in Xero app

Guidelines

  • Always refresh the access token before making API calls — Xero tokens expire after 30 minutes.
  • Use If-Modified-Since headers on GET requests to sync only updated records.
  • Xero rate limits: 60 calls/minute per tenant. Batch writes (e.g. multiple invoices in one POST) to stay within limits.
  • Account codes (e.g. "200" for Revenue, "400" for Expenses) vary by organization — confirm the chart of accounts before hardcoding.
  • For production, store tokens in a database or secrets manager, not in a local file.
  • Use AUTHORISED status for invoices to make them visible in Xero's UI immediately.
  • The Xero sandbox (demo company) is available at https://api.xero.com — connect it during OAuth to test without affecting real data.