From cbff335f98dc70be3243ddba0bb3d83ec41c3968 Mon Sep 17 00:00:00 2001 From: Shlomonhaon Date: Sun, 28 Jun 2026 14:12:34 +0300 Subject: [PATCH] Add sumit-api-integration skill Adds a developer-tools skill for integrating the SUMIT (formerly OfficeGuy) REST API into Israeli applications: authentication (Credentials in body), document creation across 23 document types, PCI-safe tokenized card charging, recurring billing, expense capture, paginated data retrieval, and Triggers/IPN webhooks. Bundle: SKILL.md, SKILL_HE.md, metadata.json, evidence.json, three reference files (full endpoint map, enums, payments flow), and a zero-dependency stdlib API client (scripts/sumit_client.py). Passes scripts/validate-skill.sh. Co-Authored-By: claude-flow --- sumit-api-integration/SKILL.md | 182 ++++++++++++++++++ sumit-api-integration/SKILL_HE.md | 169 ++++++++++++++++ sumit-api-integration/evidence.json | 47 +++++ sumit-api-integration/metadata.json | 26 +++ .../references/api-endpoints.md | 163 ++++++++++++++++ .../references/document-types-and-enums.md | 81 ++++++++ .../references/payments-and-tokenization.md | 112 +++++++++++ sumit-api-integration/scripts/sumit_client.py | 139 +++++++++++++ 8 files changed, 919 insertions(+) create mode 100644 sumit-api-integration/SKILL.md create mode 100644 sumit-api-integration/SKILL_HE.md create mode 100644 sumit-api-integration/evidence.json create mode 100644 sumit-api-integration/metadata.json create mode 100644 sumit-api-integration/references/api-endpoints.md create mode 100644 sumit-api-integration/references/document-types-and-enums.md create mode 100644 sumit-api-integration/references/payments-and-tokenization.md create mode 100644 sumit-api-integration/scripts/sumit_client.py diff --git a/sumit-api-integration/SKILL.md b/sumit-api-integration/SKILL.md new file mode 100644 index 0000000..86fc72d --- /dev/null +++ b/sumit-api-integration/SKILL.md @@ -0,0 +1,182 @@ +--- +name: sumit-api-integration +description: >- + Integrate the SUMIT (formerly OfficeGuy) REST API into applications for + Israeli invoicing, accounting, and business automation. Use when user asks to + connect to SUMIT, "samit", "officeguy", create an invoice via SUMIT, "hashbonit", + charge a credit card, "slikat ashrai", pull income or expense reports, "shaivat + dohot", manage customers, or set up SUMIT webhooks. Covers authentication + (CompanyID + APIKey), document creation (23 document types), tokenized card + charging, recurring billing, expense ingestion, data retrieval, and Triggers/IPN + webhooks. Do NOT use for Green Invoice/Morning (use green-invoice), Tranzila (use + tranzila-payment-gateway), or SHAAM e-invoice allocation (use israeli-e-invoice). +license: MIT +allowed-tools: 'Bash(python:*) Bash(curl:*)' +compatibility: >- + No SDK required, uses plain HTTP/JSON over stdlib. Requires network access to + api.sumit.co.il. Works with Claude Code, Claude.ai, Cursor. +--- + +# SUMIT API Integration + +## Overview + +SUMIT (formerly OfficeGuy) is an Israeli cloud business-management platform: invoicing, credit-card charging, recurring billing, CRM, and expense capture. This skill covers the public REST API at `https://api.sumit.co.il`, which exposes 84 operations across 27 modules. + +Two facts shape every call: +1. **Every endpoint is `POST`** with a JSON body, even reads (list/get). There is no GET/PUT/DELETE per resource. +2. **Authentication is a `Credentials` object inside the JSON body** (`CompanyID` + `APIKey`), not an HTTP header. + +## Instructions + +### Step 1: Obtain credentials + +From the SUMIT dashboard: Settings, then API keys. You need three values, stored as environment variables (never hardcode them): + +| Value | Env var | Where it goes | +|-------|---------|---------------| +| Company ID | `SUMIT_COMPANY_ID` | every request body | +| API key (secret) | `SUMIT_API_KEY` | server-side requests only | +| API public key | `SUMIT_API_PUBLIC_KEY` | browser tokenization only | + +The secret `APIKey` must never reach the browser. The public key is the only credential allowed client-side. + +### Step 2: Make an authenticated call + +Base URL: `https://api.sumit.co.il`. Path pattern: `///`. Send `Content-Type: application/json`. Optional `Content-Language: he-IL` sets the response language. + +Every response uses one envelope: + +| Field | Meaning | +|-------|---------| +| `Status` | `Success` (0), `BusinessError` (1), `TechnicalError` (2) | +| `UserErrorMessage` | user-facing error text | +| `TechnicalErrorDetails` | technical detail for support | +| `Data` | the operation-specific payload | + +Critical: HTTP 200 can still carry `BusinessError`. Always check `Status`, not just the HTTP code. Use the bundled `scripts/sumit_client.py` as the single call wrapper. See `references/api-endpoints.md` for the full operation map. + +### Step 3: Create a document (invoice / receipt) + +`POST /accounting/documents/create/`. Pick a `Type` from the 23 document types (see `references/document-types-and-enums.md`). Common: `InvoiceAndReceipt` (1), `Invoice` (0), `Receipt` (2), `PriceQuotation` (12). + +```json +{ + "Credentials": { "CompanyID": 0, "APIKey": "..." }, + "Details": { + "Type": "InvoiceAndReceipt", + "Customer": { "Name": "...", "EmailAddress": "...", "SearchMode": "EmailAddress" }, + "Description": "...", "Language": "Hebrew", "Currency": "ILS" + }, + "Items": [ { "Quantity": 1, "UnitPrice": 1000, "Description": "..." } ], + "Payments":[ { "Amount": 1180, "Details_BankTransfer": {} } ], + "VATIncluded": false, "VATRate": 18 +} +``` + +Response `Data`: `DocumentID`, `DocumentNumber`, `CustomerID`, `DocumentDownloadURL`. `SearchMode` enables upsert (create-or-find), avoiding duplicate customers. + +### Step 4: Charge a credit card (PCI-safe tokenization) + +Card details must never touch your server. The flow: +1. Browser loads `https://app.sumit.co.il/scripts/payments.js`. +2. `OfficeGuy.Payments.BindFormSubmit({ CompanyID, APIPublicKey })` tokenizes the card and injects `og-token`. +3. Send `og-token` to your server, then call `POST /billing/payments/charge/` with `SingleUseToken`. + +`charge` charges and issues an invoice in one call. Key fields: `SingleUseToken`, `Items[]`, `VATIncluded`, `SendDocumentByEmail`, `AuthoriseOnly` (validate without charging, good for testing). Full flow in `references/payments-and-tokenization.md`. + +### Step 5: Recurring billing + +`POST /billing/recurring/charge/` charges and creates a standing order. Set `Items[].Item.Duration_Months` (1 = monthly), `Items[].Recurrence` (e.g. 12), `Items[].Date_Start`. Manage with `recurring/listforcustomer`, `recurring/update`, `recurring/cancel`. Store a card token via `billing/paymentmethods/setforcustomer` for repeat charges. + +### Step 6: Capture expenses + +`POST /accounting/documents/addexpense/` pushes a supplier expense with a Base64 file (`ExpenseFile`), `Supplier`, `Lines[]`, and `Payments[]`. To pull expenses that SUMIT captured automatically (via its WhatsApp/email AI capture), call `documents/list` with expense `DocumentTypes`: `ExpenseInvoice` (16), `ExpenseInvoiceReceipt` (15), `ExpenseReceipt` (17). + +### Step 7: Retrieve data (reports) + +All reads are `POST`, all paginated (`Paging.PageSize` 10 to 1000): + +| Goal | Endpoint | +|------|----------| +| List documents | `/accounting/documents/list/` | +| Document detail | `/accounting/documents/getdetails/` | +| Document PDF | `/accounting/documents/getpdf/` | +| List payments | `/billing/payments/list/` | +| Customer debt | `/accounting/documents/getdebt/` , `/getdebtreport/` | +| List customers | `/crm/data/listentities/` (Folder=Customers, LoadProperties=true) | + +### Step 8: Receive data in real time (webhooks) + +Two push channels: +1. **Triggers**: `POST /triggers/triggers/subscribe/` with `URL`, `Folder`, `TriggerType` (`CreateOrUpdate` / `Create` / `Update` / `Archive` / `Delete`). Fires on entity changes. +2. **IPN**: pass `IPNURL` to `/billing/payments/beginredirect/` to get a server-to-server notification of a payment result. + +Verify the request origin, return HTTP 200 fast, then process asynchronously and idempotently. + +## Examples + +### Example 1: Issue a tax invoice-receipt +User says: "create a hashbonit mas-kabala for 1000 plus VAT to a client by email". +Actions: +1. Build the `documents/create` body with `Type: InvoiceAndReceipt`, `Items[].UnitPrice: 1000`, `VATIncluded: false`, `VATRate: 18`. +2. Call via `scripts/sumit_client.py`. +3. Read `Data.DocumentNumber` and `Data.DocumentDownloadURL`. +Result: invoice issued, PDF link returned. + +### Example 2: Charge a saved card monthly +User says: "set up a 99 NIS monthly slikat ashrai". +Actions: +1. Tokenize once with payments.js to get `og-token`. +2. Call `recurring/charge` with `SingleUseToken`, `Duration_Months: 1`, `Recurrence: 12`. +Result: standing order created, first invoice issued. + +### Example 3: Pull this month's expenses +User says: "shaivat dohot of this month's hotzaot". +Actions: +1. Call `documents/list` with `DocumentTypes: ["ExpenseInvoice","ExpenseInvoiceReceipt","ExpenseReceipt"]` and a `DateFrom`/`DateTo` range. +2. Page through with `Paging`. +Result: list of expense documents for the period. + +## Bundled Resources + +### Scripts +- `scripts/sumit_client.py` -- Zero-dependency (stdlib) SUMIT API client and CLI: wraps the `Credentials` envelope, posts JSON, and raises on non-Success `Status`. Run: `python scripts/sumit_client.py --help` + +### References +- `references/api-endpoints.md` -- The full map of all 84 operations across 27 modules with paths and one-line purpose. Consult when you need an endpoint not covered in the steps above. +- `references/document-types-and-enums.md` -- All 23 document types, payment types, currencies, search modes, and the response-status enum. Consult when choosing a `Type` or decoding an enum value. +- `references/payments-and-tokenization.md` -- The full PCI-safe charging flow, payments.js setup, saved cards, recurring billing, and hosted payment pages. Consult when implementing any charging path. + +## Reference Links + +Official sources for verifying and updating the information in this skill: + +| Source | URL | What to Check | +|--------|-----|---------------| +| SUMIT API (Swagger) | https://app.sumit.co.il/help/developers/swagger/index.html | Live endpoint list, request/response schemas | +| SUMIT OpenAPI spec | https://app.sumit.co.il/swagger/v1/swagger.json | Raw machine-readable spec | +| SUMIT developers help center | https://help.sumit.co.il/he/collections/3333669 | Integration guides, testing, API keys | +| Payments JavaScript API | https://help.sumit.co.il/he/articles/5893615-payments-javascript-api | Tokenization, BindFormSubmit, test cards | + +## Gotchas + +- Every operation is `POST`, including all reads. There is no REST verb per resource; a "delete entity" is `POST /crm/data/deleteentity/`. +- `Credentials` lives in the JSON body, not in an `Authorization` header. +- HTTP 200 does not mean success. Check the `Status` field on every response. +- The secret `APIKey` must stay server-side. Only `APIPublicKey` may appear in the browser. +- `ResponseLanguage` in request bodies is deprecated; use the `Content-Language` header instead. + +## Troubleshooting + +### Error: response has Status "BusinessError" +Cause: a logical problem (missing customer name, declined card, validation failure). +Solution: show `UserErrorMessage` to the user. Do not retry blindly; fix the input first. + +### Error: response has Status "TechnicalError" +Cause: a transient or system fault on the SUMIT side. +Solution: log `TechnicalErrorDetails`, retry with backoff, and report the detail to SUMIT support if it persists. + +### Error: card charge fails with no token +Cause: the server received raw card fields instead of a `SingleUseToken`. +Solution: tokenize in the browser with payments.js first; the server should only ever see `og-token`. diff --git a/sumit-api-integration/SKILL_HE.md b/sumit-api-integration/SKILL_HE.md new file mode 100644 index 0000000..48eeeed --- /dev/null +++ b/sumit-api-integration/SKILL_HE.md @@ -0,0 +1,169 @@ +--- +name: sumit-api-integration +description: >- + Integrate the SUMIT (formerly OfficeGuy) REST API into applications for + Israeli invoicing, accounting, and business automation. Use when user asks to + connect to SUMIT, "samit", "officeguy", create an invoice via SUMIT, "hashbonit", + charge a credit card, "slikat ashrai", pull income or expense reports, "shaivat + dohot", manage customers, or set up SUMIT webhooks. Covers authentication + (CompanyID + APIKey), document creation (23 document types), tokenized card + charging, recurring billing, expense ingestion, data retrieval, and Triggers/IPN + webhooks. Do NOT use for Green Invoice/Morning (use green-invoice), Tranzila (use + tranzila-payment-gateway), or SHAAM e-invoice allocation (use israeli-e-invoice). +license: MIT +allowed-tools: 'Bash(python:*) Bash(curl:*)' +compatibility: >- + No SDK required, uses plain HTTP/JSON over stdlib. Requires network access to + api.sumit.co.il. Works with Claude Code, Claude.ai, Cursor. +--- + +# אינטגרציית SUMIT API + +## סקירה + +SUMIT (לשעבר OfficeGuy) היא פלטפורמת ענן ישראלית לניהול עסקי: חשבוניות, סליקת אשראי, הוראות קבע, CRM וקליטת הוצאות. ה-skill מכסה את ה-REST API הציבורי בכתובת api.sumit.co.il, החושף 84 פעולות ב-27 מודולים. + +שני עקרונות מעצבים כל קריאה: +1. כל endpoint הוא POST עם גוף JSON, גם שליפות (list/get). אין GET/PUT/DELETE לפי משאב. +2. האימות הוא אובייקט Credentials בתוך גוף ה-JSON (CompanyID ו-APIKey), לא בכותרת HTTP. + +## הוראות + +### שלב 1: השגת מפתחות + +מתוך ממשק SUMIT: הגדרות, ואז מפתחות API. נדרשים שלושה ערכים, הנשמרים כמשתני סביבה (לעולם לא בקוד): + +- מזהה חברה: משתנה הסביבה SUMIT_COMPANY_ID. נכנס לכל בקשה. +- מפתח API סודי: משתנה הסביבה SUMIT_API_KEY. רק בקריאות צד-שרת. +- מפתח API ציבורי: משתנה הסביבה SUMIT_API_PUBLIC_KEY. רק לטוקניזציה בדפדפן. + +המפתח הסודי APIKey לעולם לא יגיע לדפדפן. המפתח הציבורי הוא היחיד המותר בצד הלקוח. + +### שלב 2: ביצוע קריאה מאומתת + +כתובת בסיס: api.sumit.co.il. תבנית נתיב: controller ואז action. שלח Content-Type של application/json. כותרת אופציונלית Content-Language בערך he-IL קובעת את שפת התשובה. + +כל תשובה משתמשת במעטפת אחת: Status (Success=0, BusinessError=1, TechnicalError=2), UserErrorMessage (טקסט שגיאה למשתמש), TechnicalErrorDetails (פירוט טכני), ו-Data (התוכן הספציפי לפעולה). + +קריטי: קוד HTTP 200 עדיין יכול לשאת BusinessError. תמיד בדוק את Status, לא רק את קוד ה-HTTP. השתמש ב-scripts/sumit_client.py כעוטף הקריאות היחיד. ראה references/api-endpoints.md למפת הפעולות המלאה. + +### שלב 3: יצירת מסמך (חשבונית או קבלה) + +נתיב create של documents. בחר Type מתוך 23 סוגי המסמך (ראה references/document-types-and-enums.md). נפוצים: InvoiceAndReceipt (1), Invoice (0), Receipt (2), PriceQuotation (12). + +```json +{ + "Credentials": { "CompanyID": 0, "APIKey": "..." }, + "Details": { + "Type": "InvoiceAndReceipt", + "Customer": { "Name": "...", "EmailAddress": "...", "SearchMode": "EmailAddress" }, + "Description": "...", "Language": "Hebrew", "Currency": "ILS" + }, + "Items": [ { "Quantity": 1, "UnitPrice": 1000, "Description": "..." } ], + "Payments":[ { "Amount": 1180, "Details_BankTransfer": {} } ], + "VATIncluded": false, "VATRate": 18 +} +``` + +התשובה ב-Data: DocumentID, DocumentNumber, CustomerID, DocumentDownloadURL. השדה SearchMode מאפשר upsert (צור-או-מצא) ומונע כפילות לקוחות. + +### שלב 4: סליקת אשראי (טוקניזציה תואמת PCI) + +פרטי הכרטיס לעולם לא נוגעים בשרת שלך. הזרימה: +1. הדפדפן טוען את payments.js מ-app.sumit.co.il. +2. הקריאה OfficeGuy.Payments.BindFormSubmit עם CompanyID ו-APIPublicKey מטקנת את הכרטיס ומזריקה og-token. +3. שלח את og-token לשרת שלך, ואז קרא ל-charge עם SingleUseToken. + +הפעולה charge מחייבת ומפיקה חשבונית בקריאה אחת. שדות מרכזיים: SingleUseToken, Items, VATIncluded, SendDocumentByEmail, AuthoriseOnly (אימות ללא חיוב, מצוין לבדיקות). הזרימה המלאה ב-references/payments-and-tokenization.md. + +### שלב 5: הוראות קבע + +הנתיב charge של recurring מחייב ויוצר הוראת קבע. הגדר Duration_Months בפריט (1 לחיוב חודשי), Recurrence (למשל 12), ו-Date_Start. נהל עם listforcustomer, update ו-cancel. שמור טוקן כרטיס דרך paymentmethods/setforcustomer לחיובים חוזרים. + +### שלב 6: קליטת הוצאות + +הנתיב addexpense דוחף הוצאת ספק עם קובץ Base64 (ExpenseFile), Supplier, Lines ו-Payments. כדי למשוך הוצאות ש-SUMIT קלטה אוטומטית (דרך קליטת ה-AI מוואטסאפ ומייל), קרא ל-documents/list עם DocumentTypes של הוצאה: ExpenseInvoice (16), ExpenseInvoiceReceipt (15), ExpenseReceipt (17). + +### שלב 7: שאיבת נתונים (דוחות) + +כל השליפות הן POST, כולן עם עימוד (Paging.PageSize בין 10 ל-1000): + +- רשימת מסמכים: documents/list +- פרטי מסמך: documents/getdetails +- PDF של מסמך: documents/getpdf +- רשימת תשלומים: payments/list +- חוב לקוח: documents/getdebt ו-getdebtreport +- רשימת לקוחות: crm/data/listentities עם Folder של Customers ו-LoadProperties אמת + +### שלב 8: קבלת נתונים בזמן אמת (webhooks) + +שני ערוצי Push: +1. Triggers: הנתיב subscribe עם URL, Folder ו-TriggerType (CreateOrUpdate, Create, Update, Archive, Delete). מופעל על שינויי ישות. +2. IPN: העברת IPNURL ל-beginredirect מחזירה התראת שרת-לשרת על תוצאת תשלום. + +אמת את מקור הבקשה, החזר HTTP 200 מהר, ואז עבד אסינכרונית ואידמפוטנטית. + +## דוגמאות + +### דוגמה 1: הפקת חשבונית מס-קבלה +המשתמש אומר: צור חשבונית מס-קבלה על 1000 בתוספת מע"מ ללקוח לפי מייל. +פעולות: +1. בנה את גוף documents/create עם Type של InvoiceAndReceipt, UnitPrice של 1000, VATIncluded שקר, VATRate של 18. +2. קרא דרך scripts/sumit_client.py. +3. קרא את DocumentNumber ו-DocumentDownloadURL מ-Data. +תוצאה: החשבונית הופקה, קישור PDF הוחזר. + +### דוגמה 2: חיוב כרטיס שמור חודשי +המשתמש אומר: הקם סליקת אשראי חודשית של 99 שקלים. +פעולות: +1. טקן פעם אחת עם payments.js לקבלת og-token. +2. קרא ל-recurring/charge עם SingleUseToken, Duration_Months של 1, Recurrence של 12. +תוצאה: הוראת קבע נוצרה, החשבונית הראשונה הופקה. + +### דוגמה 3: שאיבת הוצאות החודש +המשתמש אומר: שאיבת דוחות של הוצאות החודש. +פעולות: +1. קרא ל-documents/list עם DocumentTypes של ExpenseInvoice, ExpenseInvoiceReceipt, ExpenseReceipt וטווח DateFrom ו-DateTo. +2. דפדף עם Paging. +תוצאה: רשימת מסמכי הוצאה לתקופה. + +## משאבים מצורפים + +### סקריפטים +- scripts/sumit_client.py -- לקוח API ו-CLI ל-SUMIT ללא תלויות (stdlib בלבד): עוטף את מעטפת ה-Credentials, שולח JSON, וזורק שגיאה כש-Status אינו Success. הרצה: python scripts/sumit_client.py --help + +### מסמכי עזר +- references/api-endpoints.md -- מפת כל 84 הפעולות ב-27 המודולים עם נתיבים ותיאור שורה. עיין כשצריך endpoint שלא מכוסה בשלבים. +- references/document-types-and-enums.md -- כל 23 סוגי המסמך, אמצעי תשלום, מטבעות, מצבי חיפוש ו-enum הסטטוס. עיין בבחירת Type או בפענוח ערך enum. +- references/payments-and-tokenization.md -- זרימת הסליקה התואמת PCI, הקמת payments.js, כרטיסים שמורים, הוראות קבע ודפי תשלום מתארחים. עיין במימוש כל מסלול סליקה. + +## קישורי עזר + +מקורות רשמיים לאימות ועדכון המידע ב-skill: + +- SUMIT API (Swagger): https://app.sumit.co.il/help/developers/swagger/index.html -- רשימת endpoints חיה וסכמות +- מפרט OpenAPI: https://app.sumit.co.il/swagger/v1/swagger.json -- spec גולמי קריא-מכונה +- מרכז המידע למפתחים: https://help.sumit.co.il/he/collections/3333669 -- מדריכי אינטגרציה, בדיקות, מפתחות +- Payments JavaScript API: https://help.sumit.co.il/he/articles/5893615-payments-javascript-api -- טוקניזציה, BindFormSubmit, כרטיסי בדיקה + +## נקודות חשובות + +- כל פעולה היא POST, כולל כל השליפות. אין פועל REST לפי משאב; מחיקת ישות היא POST לנתיב crm/data/deleteentity. +- Credentials נמצא בגוף ה-JSON, לא בכותרת Authorization. +- HTTP 200 אינו אומר הצלחה. בדוק את שדה Status בכל תשובה. +- המפתח הסודי APIKey נשאר בצד-שרת. רק APIPublicKey מותר בדפדפן. +- השדה ResponseLanguage בגוף הבקשה מיושן; השתמש בכותרת Content-Language במקום. + +## פתרון בעיות + +### שגיאה: התשובה עם Status של BusinessError +סיבה: בעיה לוגית (חסר שם לקוח, אשראי נדחה, כשל ולידציה). +פתרון: הצג את UserErrorMessage למשתמש. אל תנסה שוב באופן עיוור; תקן את הקלט קודם. + +### שגיאה: התשובה עם Status של TechnicalError +סיבה: תקלה זמנית או מערכתית בצד SUMIT. +פתרון: רשום ללוג את TechnicalErrorDetails, נסה שוב עם backoff, ודווח לתמיכת SUMIT אם נמשך. + +### שגיאה: חיוב הכרטיס נכשל ללא טוקן +סיבה: השרת קיבל שדות כרטיס גולמיים במקום SingleUseToken. +פתרון: טקן בדפדפן עם payments.js קודם; השרת צריך לראות רק את og-token. diff --git a/sumit-api-integration/evidence.json b/sumit-api-integration/evidence.json new file mode 100644 index 0000000..3760674 --- /dev/null +++ b/sumit-api-integration/evidence.json @@ -0,0 +1,47 @@ +{ + "schemaVersion": "1.0", + "skill": "sumit-api-integration", + "generated_at": "2026-06-28T00:00:00Z", + "claims": [ + { + "claim_id": "base-url-and-post-convention", + "claim": "The SUMIT REST API base URL is https://api.sumit.co.il and every operation is an HTTP POST with a JSON body, including reads such as list and get.", + "source_url": "https://app.sumit.co.il/swagger/v1/swagger.json", + "raw_snippet": "\"servers\": [ { \"url\": \"https://api.sumit.co.il\" } ] ... \"post\"", + "fetched_at": "2026-06-28T00:00:00Z", + "appears_in": ["SKILL.md", "SKILL_HE.md", "references/api-endpoints.md"] + }, + { + "claim_id": "credentials-in-body", + "claim": "Authentication is a Credentials object (CompanyID and APIKey) placed inside the JSON request body, not in an HTTP Authorization header.", + "source_url": "https://app.sumit.co.il/swagger/v1/swagger.json", + "raw_snippet": "Core_APICredentials: required [APIKey, CompanyID]", + "fetched_at": "2026-06-28T00:00:00Z", + "appears_in": ["SKILL.md", "SKILL_HE.md", "scripts/sumit_client.py"] + }, + { + "claim_id": "response-envelope", + "claim": "Every response uses one envelope with Status (Success, BusinessError, TechnicalError), UserErrorMessage, TechnicalErrorDetails, and Data. HTTP 200 can still carry a BusinessError, so callers must check Status.", + "source_url": "https://app.sumit.co.il/swagger/v1/swagger.json", + "raw_snippet": "Teva.Common.ResponseStatus enum: Success (0), BusinessError (1), TechnicalError (2)", + "fetched_at": "2026-06-28T00:00:00Z", + "appears_in": ["SKILL.md", "SKILL_HE.md", "scripts/sumit_client.py"] + }, + { + "claim_id": "document-types", + "claim": "The SUMIT documents/create operation accepts 23 document types numbered 0 to 22, including Invoice (0), InvoiceAndReceipt (1), Receipt (2), PriceQuotation (12), and expense types ExpenseInvoiceReceipt (15), ExpenseInvoice (16), ExpenseReceipt (17).", + "source_url": "https://app.sumit.co.il/swagger/v1/swagger.json", + "raw_snippet": "Accounting_Typed_DocumentType enum: Invoice (0) ... SupplierPayment (22)", + "fetched_at": "2026-06-28T00:00:00Z", + "appears_in": ["SKILL.md", "references/document-types-and-enums.md"] + }, + { + "claim_id": "payments-js-tokenization", + "claim": "Credit-card charging uses a browser tokenization flow: payments.js with OfficeGuy.Payments.BindFormSubmit (CompanyID + APIPublicKey) produces a single-use og-token, which the server passes as SingleUseToken to billing/payments/charge, so raw card data never reaches the server.", + "source_url": "https://help.sumit.co.il/he/articles/5893615-payments-javascript-api", + "raw_snippet": "OfficeGuy.Payments.BindFormSubmit ... og-token ... SingleUseToken", + "fetched_at": "2026-06-28T00:00:00Z", + "appears_in": ["SKILL.md", "SKILL_HE.md", "references/payments-and-tokenization.md"] + } + ] +} diff --git a/sumit-api-integration/metadata.json b/sumit-api-integration/metadata.json new file mode 100644 index 0000000..29e48dc --- /dev/null +++ b/sumit-api-integration/metadata.json @@ -0,0 +1,26 @@ +{ + "author": "SHLOMO NAHON", + "version": "1.0.0", + "category": "developer-tools", + "tags": { + "he": ["SUMIT", "API", "חשבוניות", "סליקה", "ישראל"], + "en": ["SUMIT", "API", "invoicing", "payments", "israel"] + }, + "display_name": { + "he": "אינטגרציית SUMIT API", + "en": "SUMIT API Integration" + }, + "display_description": { + "he": "חיבור ה-API של SUMIT (OfficeGuy) לאפליקציות: הפקת חשבוניות וקבלות, סליקת אשראי, הוראות קבע, קליטת הוצאות, שאיבת דוחות ו-webhooks לעסקים בישראל.", + "en": "Integrate the SUMIT (OfficeGuy) REST API: invoicing, receipts, card charging, recurring billing, expense capture, data retrieval, and webhooks for Israeli businesses." + }, + "supported_agents": [ + "claude-code", + "cursor", + "github-copilot", + "windsurf", + "opencode", + "codex", + "gemini-cli" + ] +} diff --git a/sumit-api-integration/references/api-endpoints.md b/sumit-api-integration/references/api-endpoints.md new file mode 100644 index 0000000..ec706c8 --- /dev/null +++ b/sumit-api-integration/references/api-endpoints.md @@ -0,0 +1,163 @@ +# SUMIT API: full endpoint map + +All endpoints are `POST` to `https://api.sumit.co.il` with a JSON body that includes the `Credentials` object. 84 operations across 27 modules. Consult this when you need an operation not covered in the main SKILL.md steps. + +## Accounting: Documents +| Path | Purpose | +|------|---------| +| `/accounting/documents/create/` | Create a document (invoice, receipt, quotation, etc.) | +| `/accounting/documents/list/` | List documents by type, number range, or date range | +| `/accounting/documents/getdetails/` | Full detail of one document | +| `/accounting/documents/getpdf/` | Document PDF | +| `/accounting/documents/send/` | Email a document | +| `/accounting/documents/cancel/` | Cancel an existing document | +| `/accounting/documents/movetobooks/` | Finalize a draft (move to books) | +| `/accounting/documents/addexpense/` | Add a supplier expense with a Base64 file | +| `/accounting/documents/getdebt/` | Single customer debt | +| `/accounting/documents/getdebtreport/` | Customers debt report | + +## Accounting: Customers +| Path | Purpose | +|------|---------| +| `/accounting/customers/create/` | Create or find a customer (SearchMode) | +| `/accounting/customers/update/` | Update or find a customer | +| `/accounting/customers/createremark/` | Add a remark to a customer | +| `/accounting/customers/getdetailsurl/` | Link to the customer page | + +## Accounting: General +| Path | Purpose | +|------|---------| +| `/accounting/general/getvatrate/` | VAT rate by date | +| `/accounting/general/getexchangerate/` | Foreign currency exchange rate | +| `/accounting/general/getnextdocumentnumber/` | Next document number for a type | +| `/accounting/general/setnextdocumentnumber/` | Set the next document number | +| `/accounting/general/verifybankaccount/` | Verify bank account details | +| `/accounting/general/updatesettings/` | Update accounting settings | + +## Accounting: Income items +| Path | Purpose | +|------|---------| +| `/accounting/incomeitems/create/` | Create a catalog item (product/service) | +| `/accounting/incomeitems/list/` | List catalog items | + +## Books +| Path | Purpose | +|------|---------| +| `/books/transactions/createbatch/` | Create a batch of journal transactions | + +## Payments +| Path | Purpose | +|------|---------| +| `/billing/payments/charge/` | Charge a customer and issue an invoice | +| `/billing/payments/multivendorcharge/` | Split charge across vendors (per-item credentials) | +| `/billing/payments/beginredirect/` | Start a hosted payment page (supports IPNURL) | +| `/billing/payments/get/` | Single payment detail | +| `/billing/payments/list/` | List payments | + +## Payment methods (saved cards) +| Path | Purpose | +|------|---------| +| `/billing/paymentmethods/setforcustomer/` | Store a payment method (card token) on a customer | +| `/billing/paymentmethods/getforcustomer/` | Get a customer payment method | +| `/billing/paymentmethods/remove/` | Remove a payment method | + +## Recurring +| Path | Purpose | +|------|---------| +| `/billing/recurring/charge/` | Charge and create a standing order | +| `/billing/recurring/listforcustomer/` | List a customer recurring items | +| `/billing/recurring/update/` | Update a recurring item | +| `/billing/recurring/cancel/` | Cancel a recurring item | +| `/billing/recurring/updatesettings/` | Recurring billing settings | + +## General billing (Upay) +| Path | Purpose | +|------|---------| +| `/billing/generalbilling/openupayterminal/` | Open an instant Upay terminal | +| `/billing/generalbilling/setupaycredentials/` | Link an existing Upay account | + +## Credit card terminal: Gateway +| Path | Purpose | +|------|---------| +| `/creditguy/gateway/transaction/` | Direct card transaction (rare; prefer payments/charge) | +| `/creditguy/gateway/gettransaction/` | Existing transaction detail (public key) | +| `/creditguy/gateway/getreferencenumbers/` | Reference numbers for transactions (public key) | +| `/creditguy/gateway/beginredirect/` | Start a redirect transaction | + +## Credit card terminal: Vault (tokenization) +| Path | Purpose | +|------|---------| +| `/creditguy/vault/tokenize/` | Permanent token (card number to token) | +| `/creditguy/vault/tokenizesingleuse/` | Single-use token (multipart form) | +| `/creditguy/vault/tokenizesingleusejson/` | Single-use token (JSON) | + +## Credit card terminal: Billing (batch) +| Path | Purpose | +|------|---------| +| `/creditguy/billing/load/` | Load batch transactions | +| `/creditguy/billing/process/` | Process the loaded batch (cannot be stopped) | +| `/creditguy/billing/getstatus/` | Batch process status | + +## CRM: Data +| Path | Purpose | +|------|---------| +| `/crm/data/createentity/` | Create an entity | +| `/crm/data/updateentity/` | Update an entity (CreateIfMissing optional) | +| `/crm/data/getentity/` | Get an entity | +| `/crm/data/listentities/` | List entities (filters, order, paging, LoadProperties) | +| `/crm/data/archiveentity/` | Archive an entity | +| `/crm/data/deleteentity/` | Delete an entity | +| `/crm/data/countentityusage/` | Count entity usage | +| `/crm/data/getentityprinthtml/` | HTML to print one entity | +| `/crm/data/getentitieshtml/` | HTML to print many entities | + +## CRM: Schema and Views +| Path | Purpose | +|------|---------| +| `/crm/schema/getfolder/` | Folder (entity type) schema | +| `/crm/schema/listfolders/` | List folders | +| `/crm/views/listviews/` | List views | + +## SMS +| Path | Purpose | +|------|---------| +| `/sms/sms/send/` | Send one SMS | +| `/sms/sms/sendmultiple/` | Send multiple SMS | +| `/sms/sms/listsenders/` | List approved sender names | +| `/sms/mailinglists/add/` | Add a recipient to an SMS list | +| `/sms/mailinglists/list/` | List SMS mailing lists | + +## Email subscriptions +| Path | Purpose | +|------|---------| +| `/emailsubscriptions/mailinglists/add/` | Add a recipient to an email list | +| `/emailsubscriptions/mailinglists/list/` | List email mailing lists | + +## Triggers (webhooks) +| Path | Purpose | +|------|---------| +| `/triggers/triggers/subscribe/` | Subscribe a webhook URL to a folder and trigger type | +| `/triggers/triggers/unsubscribe/` | Remove a webhook | + +## Website (organizations) +| Path | Purpose | +|------|---------| +| `/website/companies/create/` | Create a new organization | +| `/website/companies/update/` | Update organization details | +| `/website/companies/getdetails/` | Organization details | +| `/website/companies/installapplications/` | Install applications | +| `/website/companies/listquotas/` | Usage quotas | +| `/website/users/create/` | Create a user and grant permissions | +| `/website/users/loginredirect/` | Login via redirect without exposing credentials | +| `/website/permissions/set/` | Grant a user permission | +| `/website/permissions/remove/` | Remove a user permission | + +## Other modules +| Path | Purpose | +|------|---------| +| `/stock/stock/list/` | List stock levels | +| `/deals/adddeal/` | Create a deal (and optionally a customer) | +| `/deals/createremark/` | Add a remark to a deal | +| `/customerservice/tickets/create/` | Open a support ticket | +| `/fax/fax/send/` | Send an outgoing fax | +| `/scheduleddocuments/documents/createfromdocument/` | Schedule a document from a template document | diff --git a/sumit-api-integration/references/document-types-and-enums.md b/sumit-api-integration/references/document-types-and-enums.md new file mode 100644 index 0000000..89abbb5 --- /dev/null +++ b/sumit-api-integration/references/document-types-and-enums.md @@ -0,0 +1,81 @@ +# SUMIT enums: document types, payment types, currencies, search modes + +Consult this when choosing a `Type` for `documents/create` or decoding an enum returned by the API. Values may be sent as the string name or the integer in parentheses. + +## Document types (`Details.Type`) +| Name | Int | Hebrew | +|------|-----|--------| +| Invoice | 0 | חשבונית עסקה | +| InvoiceAndReceipt | 1 | חשבונית מס/קבלה | +| Receipt | 2 | קבלה | +| ProformaInvoice | 3 | חשבונית פרופורמה | +| DonationReceipt | 4 | קבלה על תרומה | +| CreditInvoice | 5 | חשבונית זיכוי | +| CreditInvoiceAndReceipt | 6 | חשבונית מס/קבלה זיכוי | +| CreditReceipt | 7 | קבלת זיכוי | +| Order | 8 | הזמנה | +| DeliveryNote | 9 | תעודת משלוח | +| GoodsReturnNote | 10 | תעודת החזרה | +| PurchasingOrder | 11 | הזמנת רכש | +| PriceQuotation | 12 | הצעת מחיר | +| PaymentRequest | 13 | דרישת תשלום | +| CreditDonationReceipt | 14 | זיכוי קבלת תרומה | +| ExpenseInvoiceReceipt | 15 | חשבונית/קבלה הוצאה | +| ExpenseInvoice | 16 | חשבונית הוצאה | +| ExpenseReceipt | 17 | קבלת הוצאה | +| ExpenseRequest | 18 | בקשת הוצאה | +| CreditExpenseInvoiceReceipt | 19 | זיכוי חשבונית/קבלה הוצאה | +| CreditExpenseInvoice | 20 | זיכוי חשבונית הוצאה | +| CreditExpenseReceipt | 21 | זיכוי קבלת הוצאה | +| SupplierPayment | 22 | תשלום לספק | + +Income document types (0 to 14) are for money coming in. Expense document types (15 to 22) are for money going out. + +## Document payment types (`Payments[].Type`) +| Name | Int | +|------|-----| +| Automatic | 0 | +| General | 1 | +| Cash | 2 | +| BankTransfer | 3 | +| Cheque | 4 | +| CreditCard | 5 | +| Digital | 6 | +| TaxWithholding | 7 | +| Other | 8 | + +Each `Payments[]` row carries exactly one matching details object: `Details_Cash`, `Details_BankTransfer`, `Details_Cheque`, `Details_CreditCard`, `Details_Digital`, `Details_TaxWithholding`, `Details_General`, or `Details_Other`. + +## Saved payment method type (`PaymentMethod.Type`) +| Name | Int | +|------|-----| +| Other | 0 | +| CreditCard | 1 | +| DirectDebit | 2 | + +DirectDebit (bank standing order) uses `DirectDebit_Bank`, `DirectDebit_Branch`, `DirectDebit_Account`. + +## Customer search mode (`Customer.SearchMode`) +Enables create-or-find (upsert) so you avoid duplicate customers. +| Name | Int | Matches on | +|------|-----|-----------| +| Automatic | 0 | best guess from supplied fields | +| None | 1 | always create new (default) | +| ExternalIdentifier | 2 | your external id | +| Name | 3 | name | +| CompanyNumber | 4 | tax/company number | +| Phone | 5 | phone | +| EmailAddress | 6 | email | + +## Response status (`Status`) +| Name | Int | Meaning | +|------|-----|---------| +| Success | 0 | operation succeeded | +| BusinessError | 1 | logical/validation problem, show UserErrorMessage | +| TechnicalError | 2 | system fault, log and retry | + +## Currencies (`Currency`) +Common ISO 4217 codes, sent by name: `ILS` (default), `USD`, `EUR`, `GBP`, `AUD`, `CAD`, `CHF`, `JPY`. About 160 currencies are supported. Leave empty to use the company default currency. + +## Trigger types (`triggers/subscribe`) +`CreateOrUpdate`, `Create`, `Update`, `Archive`, `Delete`. diff --git a/sumit-api-integration/references/payments-and-tokenization.md b/sumit-api-integration/references/payments-and-tokenization.md new file mode 100644 index 0000000..39012d7 --- /dev/null +++ b/sumit-api-integration/references/payments-and-tokenization.md @@ -0,0 +1,112 @@ +# SUMIT payments: PCI-safe tokenization, charging, recurring, hosted pages + +Consult this when implementing any charging path. The guiding rule: raw card details must never reach your server. SUMIT tokenizes in the browser, and your server only ever handles a single-use token. + +## The tokenization flow + +``` +Browser --card fields--> payments.js (SUMIT, client side) +payments.js --tokenize (public key)--> SUMIT Vault +SUMIT Vault --og-token (single use)--> Browser +Browser --og-token--> Your server +Your server --charge (secret key + SingleUseToken)--> SUMIT /billing/payments/charge/ +``` + +## Step 1: load payments.js and bind the form + +```html + + +``` + +Build an HTML form with `data-og` attributes. On submit, payments.js tokenizes and injects a hidden `og-token` field: + +```html +
+ + + + + + +
+``` + +## Step 2: charge on the server + +`POST /billing/payments/charge/` with the secret key and the `SingleUseToken` you received: + +```json +{ + "Credentials": { "CompanyID": 0, "APIKey": "..." }, + "Customer": { "Name": "...", "EmailAddress": "..." }, + "SingleUseToken": "og-token-from-frontend", + "Items": [ { "Quantity": 1, "UnitPrice": 99.9, "Description": "..." } ], + "VATIncluded": true, + "SendDocumentByEmail": true +} +``` + +Useful charge fields: +- `AuthoriseOnly`: validate without actually charging (issues a draft). Good for tests. +- `AutoCapture` and `AuthorizeAmount`: two-step authorize then capture. +- `Payments_Count`, `Payments_Credit`: installments. +- `PreventDocumentCreation`: charge without issuing a document. +- `MerchantNumber`: a specific terminal. + +Response `Data` includes `Payment.ID`, `Payment.AuthNumber`, `Payment.PaymentMethod.CreditCard_Token` (for future charges), `DocumentID`, `DocumentNumber`, `DocumentDownloadURL`. + +## Direct JSON tokenization (custom UI) + +If you build your own UI, tokenize directly with the public key via `POST /creditguy/vault/tokenizesingleusejson/`: + +```json +{ + "Credentials": { "CompanyID": 0, "APIPublicKey": "..." }, + "CardNumber": "____", "ExpirationMonth": 12, "ExpirationYear": 2030, + "CVV": "___", "CitizenID": "_________" +} +``` + +Returns `Data.SingleUseToken`. + +## Saved cards (charge again later) + +- `POST /billing/paymentmethods/setforcustomer/` stores a method on a customer (pass `SingleUseToken` or a `PaymentMethod` with a token). +- `POST /billing/paymentmethods/getforcustomer/` returns the masked method plus token. +- `POST /billing/paymentmethods/remove/` removes it. + +## Recurring billing + +`POST /billing/recurring/charge/` charges now and creates a standing order. Recurring-specific fields: +- `Items[].Item.Duration_Months` (1 = monthly) or `Duration_Days`. +- `Items[].Recurrence` (e.g. 12 for a year of monthly charges). +- `Items[].Date_Start` (first charge date). +- `DocumentType` (the document issued per charge). +- `OnlyDocument` (issue documents without charging). + +## Hosted payment page (lowest PCI burden) + +`POST /billing/payments/beginredirect/` returns a hosted page URL so SUMIT hosts the card form entirely. Key fields: +- `RedirectURL`, `CancelRedirectURL`: where to send the user after success or cancel. +- `ExternalIdentifier`: your reference for matching and dedup. +- `MaximumPayments`, `ExpirationHours`, `Theme`, `Language`. +- `IPNURL`: a server-to-server notification of the payment result. This is the webhook for hosted-page charges. +- `PreventSavingPaymentMethod`. + +## Testing + +- Use `AuthoriseOnly: true` to exercise the charge path without real money (issues a draft). +- The developers help center provides test cards for integration testing. +- The free tier allows up to 10 documents per month at no cost. + +## Security notes + +- The secret `APIKey` is server-side only. The browser may only see `APIPublicKey`. +- Keep an `ExternalIdentifier` per charge and check `payments/list` before re-charging to avoid duplicates on retry. +- Add backoff and retry on `TechnicalError`. Never retry on `BusinessError`; fix the input instead. diff --git a/sumit-api-integration/scripts/sumit_client.py b/sumit-api-integration/scripts/sumit_client.py new file mode 100644 index 0000000..3467e54 --- /dev/null +++ b/sumit-api-integration/scripts/sumit_client.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +"""Minimal, zero-dependency client for the SUMIT (OfficeGuy) REST API. + +Wraps the SUMIT request convention so callers do not repeat boilerplate: + - injects the Credentials object (CompanyID + APIKey) into every body + - POSTs JSON to https://api.sumit.co.il/ + - raises on a non-Success Status, even when HTTP is 200 + +Credentials are read from environment variables. Nothing is hardcoded: + SUMIT_COMPANY_ID your numeric company id + SUMIT_API_KEY your secret API key (server-side only) + +Usage: + First set SUMIT_COMPANY_ID and SUMIT_API_KEY as environment variables, then: + python scripts/sumit_client.py --path /accounting/documents/list/ \ + --body '{"DocumentTypes":["InvoiceAndReceipt"],"Paging":{"StartIndex":0,"PageSize":50}}' + python scripts/sumit_client.py --path /accounting/documents/list/ --body-file body.json + python scripts/sumit_client.py --help + +Uses only the Python standard library (urllib). No pip install needed. +""" +import argparse +import json +import os +import sys +import urllib.request +import urllib.error + +BASE_URL = "https://api.sumit.co.il" + + +class SumitError(Exception): + """Raised when SUMIT returns a non-Success Status.""" + + +def load_credentials(): + """Read CompanyID and the secret key from the environment.""" + company_id = os.environ.get("SUMIT_COMPANY_ID", "").strip() + secret = os.environ.get("SUMIT_API_KEY", "").strip() + if not company_id or not secret: + raise SystemExit( + "Missing credentials. Set SUMIT_COMPANY_ID and SUMIT_API_KEY " + "in the environment before running." + ) + return {"CompanyID": int(company_id), "APIKey": secret} + + +def call_sumit(path, payload, credentials=None, language="he-IL", timeout=30): + """POST a JSON body to a SUMIT endpoint and return the Data object. + + The Credentials object is merged into the payload automatically. + Raises SumitError if the response Status is not Success. + """ + creds = credentials or load_credentials() + body = dict(payload or {}) + body["Credentials"] = creds + + url = BASE_URL + path + data = json.dumps(body, ensure_ascii=False).encode("utf-8") + req = urllib.request.Request(url, data=data, method="POST") + req.add_header("Content-Type", "application/json") + req.add_header("Accept", "application/json") + if language: + req.add_header("Content-Language", language) + + try: + with urllib.request.urlopen(req, timeout=timeout) as resp: + raw = resp.read().decode("utf-8") + except urllib.error.HTTPError as exc: + raw = exc.read().decode("utf-8", errors="replace") + except urllib.error.URLError as exc: + raise SumitError("Network error reaching SUMIT: " + str(exc.reason)) + + try: + parsed = json.loads(raw) + except ValueError: + raise SumitError("Non-JSON response from SUMIT: " + raw[:300]) + + status = str(parsed.get("Status", "")) + if not status.startswith("Success"): + message = ( + parsed.get("UserErrorMessage") + or parsed.get("TechnicalErrorDetails") + or "unknown error" + ) + raise SumitError("SUMIT " + status + ": " + str(message)) + return parsed.get("Data") + + +def _read_body(args): + if args.body_file: + with open(args.body_file, "r", encoding="utf-8") as handle: + return json.load(handle) + if args.body: + return json.loads(args.body) + return {} + + +def main(): + parser = argparse.ArgumentParser( + description="Call a SUMIT (OfficeGuy) API endpoint with the Credentials envelope handled for you.", + ) + parser.add_argument( + "--path", + required=True, + help="Endpoint path, for example /accounting/documents/list/", + ) + parser.add_argument( + "--body", + default="", + help="Request body as an inline JSON string (without Credentials).", + ) + parser.add_argument( + "--body-file", + default="", + help="Path to a JSON file holding the request body (without Credentials).", + ) + parser.add_argument( + "--language", + default="he-IL", + help="Content-Language header value (default he-IL).", + ) + args = parser.parse_args() + + try: + payload = _read_body(args) + data = call_sumit(args.path, payload, language=args.language) + except SumitError as exc: + print("ERROR: " + str(exc), file=sys.stderr) + sys.exit(1) + except ValueError as exc: + print("ERROR: invalid JSON body: " + str(exc), file=sys.stderr) + sys.exit(2) + + print(json.dumps(data, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main()