Integrations & API
No rip-and-replace. It works from what your ERP already exports.
Start with the item file your ERP already produces and the mail you already get. When you want automation, there is a REST API and signed webhooks — and a person on your team still approves every quote.
After a person approves
request.approved → HTTP 200
or download RQ-0142.xlsx
Reads the item exports and reports of the systems distributors run
- Eclipse
- Prophet 21
- SX.e · CloudSuite
- Dynamics 365
- NetSuite
- SAP
Product names and logos are trademarks of their owners. Goodshed reads the files these systems export; it is not affiliated with, endorsed by, or natively connected to them.
Three hand-offs, and a light on each.
Catalog in, requests in, approved drafts out. The Integrations page shows each one, how it is connected, and when it last moved.
Automate every hand-off — catalog in, requests in, approved drafts out to your ERP — while a person on your team still approves every quote.
- Catalog in12,840 itemslast import 2:04 AM · emailed export
- Requests inBy emaillast request 7:12 AM
- Drafts outWebhook on approvallast delivery 7:41 AM → 200
1 · Which system do you run?
Shapes the setup guidance · change it any timeGoodshed has no native connector to any ERP. Your choice only changes the guidance below; the paths — exports, emailed files, the API and a webhook — are the same for every system.
2Catalog in
Eclipse runs reports on a schedule and can email the result. Ask your Eclipse administrator to schedule the product file (item number, description, manufacturer, manufacturer part, UOM, sell price, on-hand) as a CSV, emailed nightly to your catalog address.
Easiest — have the system email the export here, nightly
catalog-7k2m4x@in.goodshed.aiItems update in place.
3Requests in
Most Eclipse desks quote from an inbox. Add a rule that forwards contractor RFQs to your request address, or forward by hand until the rule exists.
Easiest — forward customer emails here
rq-7k2m4x@in.goodshed.aiOr another system posts it to the API.
4Approved drafts out
The moment a person approves a draft, Goodshed sends the whole thing to a URL you choose. Nothing fires before a person decides.
https://erp-bridge.northlinesupply.com/goodshed
request.approved · signed
No middleware yet? Every request downloads as CSV or Excel for the import your ERP already has.
Goodshed has no native connector to any ERP, and does not need one to start. Choosing your system only changes the setup guidance; the connection paths are the same for everyone.
Files in. Drafts out.
Everything here works with what a distributor already has: an item export, a mailbox, and — when you are ready — someone who can call an API.
items_export.xlsx
Columns guessed from the headersYour catalog, from the file you already export
Upload the CSV or Excel item export your ERP produces. Goodshed guesses which column is which from the headers — “Item #”, “Desc”, “Mfr Part”, “Sell” — and a person confirms the mapping before anything is imported. Items are upserted by item number, so next month’s export updates prices and stock without duplicating anything. Leading zeros stay, and long numbers stay whole.
Scheduled report · 2:00 AM
To catalog-7k2m4x@in.goodshed.ai
items_nightly.csv · 18.4 MB
Catalog · imports
Or let the ERP email it, every night
Each workspace has a second address for catalog files. Most ERPs can schedule a report and email it; point that report here and every attachment is imported automatically, up to 60 MB. The same delivery twice is recognized, and a file that fails is listed on the Catalog page with the reason.
POST/api/v1/requests
{ "customer": "ABC Electric",
"project": "Lakeview Hospital Ph 2",
"files": [{ "filename": "takeoff.xlsx", … }] }202 Accepted
{ "number": "RQ-0142", "status": "PROCESSING" }
Requests in over the API
Another system can hand Goodshed a request — customer, project, text and files — in one JSON call. It is accepted at once with a request number, lands in the same inbox as forwarded mail, and is read the same way.
X-Goodshed-Event: request.approved
X-Goodshed-Timestamp: 1790086871
X-Goodshed-Signature: sha256=9f2c41e0b7…
Approved drafts out, signed
When a person approves a draft, Goodshed posts the whole thing to a URL you choose, signed so your side can prove it came from your workspace. Your middleware creates the quote in the ERP. Nothing fires before a person decides.
From the export to the approved draft.
Five steps, each with an easy path and an automated one. You can stop at the easy path and still never retype a quote.
Which system do you run?
- Epicor Eclipse
- Epicor Prophet 21
- Infor SX.e
- Microsoft Dynamics 365
- Oracle NetSuite
- SAP
- …Another system
It changes the guidance, not the connection — the paths are the same for every system.
Pick Epicor Eclipse, Epicor Prophet 21, Infor, Microsoft Dynamics 365, Oracle NetSuite, SAP or another system. The choice shapes the setup guidance — how that system usually schedules an export — and nothing else. There is no connector to install.
Email the item export to your catalog address nightly, have a job POST the file to the catalog API, or upload it on the Catalog page. Only the item number and description columns are required; manufacturer, manufacturer part, unit, category, price and quantity on hand are used when present.
Forward contractor RFQs to your request address, send them over the API, or paste one by hand. Every door leads to the same inbox, and quote requests become drafts on their own.
A named person on your team reviews the flagged lines and approves the draft. Goodshed never approves a quote and never sends one to a customer.
The request.approved webhook carries the full draft to your middleware, or a rep downloads it as CSV or Excel — one row per line with the chosen item number — and imports it the way your ERP already imports files.
API reference
A small API you can read in one sitting.
Four endpoints and three webhook events, authenticated with a workspace key. Every response is JSON in snake_case, with numbers as numbers and the same line statuses the Quote Desk shows.
| POST | /api/v1/requests | Send a request: customer, project, subject, text and up to 8 files as base64. Answers 202 with the request id and number. |
| GET | /api/v1/requests/{id} | The draft, line by line. status is PROCESSING until the draft is ready; poll, or subscribe to the webhooks. |
| POST | /api/v1/catalog/import | The ERP’s export file as the raw request body, CSV or Excel, up to 60 MB. Columns are recognized from the headers. |
| POST | /api/v1/catalog/items | Items as JSON, up to 5,000 per call, upserted by item number. |
POSTSend a request
curl -X POST "https://goodshed.ai/api/v1/requests" \
-H "Authorization: Bearer gs_live_…" \
-H "Content-Type: application/json" \
-d '{
"customer": "ABC Electric",
"project": "Lakeview Hospital Ph 2",
"subject": "Lakeview Hospital Ph 2 — pricing",
"text": "Can you price the attached by Thursday?",
"files": [{
"filename": "Lakeview_Ph2_takeoff.xlsx",
"content_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"content_base64": "UEsDBBQABgAIAAAAIQ…"
}]
}'202 Accepted
{
"id": "5f0c2a9e-8d1b-4c7e-9a43-2b6f1e0d7c15",
"number": "RQ-0142",
"status": "PROCESSING",
"url": "https://goodshed.ai/app/requests/5f0c2a9e-8d1b-4c7e-9a43-2b6f1e0d7c15",
"poll": "https://goodshed.ai/api/v1/requests/5f0c2a9e-8d1b-4c7e-9a43-2b6f1e0d7c15"
}POSTSend the catalog export
curl -X POST "https://goodshed.ai/api/v1/catalog/import" \
-H "Authorization: Bearer gs_live_…" \
-H "Content-Type: text/csv" \
--data-binary @items_nightly.csv
# 200 — a header the guess missed can be named: ?sku=Item%20No
{ "rows": 12840, "written": 12831, "skipped": 9,
"columns": { "sku": "Item #", "description": "Desc", "mfrPart": "Mfr Part", … } }GET/api/v1/requests/{id} · 200
{
"id": "5f0c2a9e-8d1b-4c7e-9a43-2b6f1e0d7c15",
"number": "RQ-0142",
"status": "READY",
"failure_reason": null,
"source": "API",
"customer": "ABC Electric",
"project": "Lakeview Hospital Ph 2",
"subject": "Lakeview Hospital Ph 2 — pricing",
"notes": null,
"created_at": "2026-09-22T11:12:04.000Z",
"approved_at": null,
"url": "https://goodshed.ai/app/requests/5f0c2a9e-8d1b-4c7e-9a43-2b6f1e0d7c15",
"counts": { "lines": 38, "resolved": 36, "review": 2, "unresolved": 0 },
"lines": [
{
"line_no": 1,
"requested_text": "QO 30A 2P",
"quantity": 20,
"uom": "EA",
"notes": null,
"status": "RESOLVED",
"confidence": "HIGH",
"reason": "Exact match on part number QO230 (Square D).",
"decided_by": "EXACT",
"item": {
"sku": "118204",
"description": "QO 2-pole 30A plug-on breaker",
"manufacturer": "Square D",
"mfr_part": "QO230",
"uom": "EA",
"price": 17.85,
"qty_on_hand": 84
}
},
{
"line_no": 2,
"requested_text": "#12 THHN BLK",
"quantity": 2000,
"uom": "FT",
"notes": null,
"status": "RESOLVED",
"confidence": "HIGH",
"reason": "Your team chose 402117 for these exact words for ABC Electric before — 6 times.",
"decided_by": "MEMORY",
"item": {
"sku": "402117",
"description": "#12 THHN/THWN-2 stranded, black",
"manufacturer": null,
"mfr_part": null,
"uom": "FT",
"price": 0.21,
"qty_on_hand": 27500
}
},
{
"line_no": 5,
"requested_text": "4S COVERS — SAME AS HOSPITAL",
"quantity": 45,
"uom": "EA",
"notes": null,
"status": "REVIEW",
"confidence": "MEDIUM",
"reason": "“Same as hospital” points to an earlier job, so a person picks the cover.",
"decided_by": null,
"item": null
}
]
}Webhooks
Goodshed POSTs the full draft — the same JSON the API returns, wrapped as { event, sent_at, request } — to an https URL you add. Each webhook gets its own signing secret, shown once.
request.readyDraft ready for reviewrequest.approvedDraft approved by a personrequest.failedRequest could not be processed
X-Goodshed-Signature is sha256= plus the HMAC-SHA256 of <timestamp>.<raw body> with that secret. The timestamp is inside the signature, so reject any delivery more than five minutes old and a recorded delivery cannot be replayed.
decided_by says who made each pick: EXACT (part number matched), MEMORY (your team chose this item for these words before), AI (chosen from the catalog shortlist) or USER (a person chose it on this request). Your ERP can treat them differently.
What your endpoint receives
POST /goodshed HTTP/1.1
Host: erp-bridge.northlinesupply.com
Content-Type: application/json
User-Agent: Goodshed-Webhooks/1
X-Goodshed-Event: request.approved
X-Goodshed-Timestamp: 1790091671
X-Goodshed-Signature: sha256=9f2c41e0b7d5…
{ "event": "request.approved", "sent_at": "2026-09-22T11:41:11.000Z", "request": { … } }Verify it (Node.js)
import { createHmac, timingSafeEqual } from "node:crypto";
// rawBody: the body exactly as received, before JSON parsing.
export function verify(headers, rawBody, secret) {
const timestamp = headers["x-goodshed-timestamp"];
// Older than five minutes: refuse it.
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const expected = "sha256=" +
createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
const given = String(headers["x-goodshed-signature"] ?? "");
return given.length === expected.length &&
timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}The small print, done properly.
An integration is only as good as what happens when something goes wrong. These are the guard rails.
New key — copy it now. It will not be shown again.
gs_live_Q3v9Xk2mN8pR…Keys shown once, stored hashed
A workspace key starts gs_live_ and is shown once when it is made; Goodshed keeps only its hash. Name one per system, see when each was last used, and revoke any of them.
HTTP/1.1 429 Too Many Requests
Retry-After: 600
{ "error": "rate_limited", … }
Limits that stop a runaway loop
Each workspace can send 120 API requests and 30 catalog imports an hour. Past that, the API answers 429 with Retry-After, so a leaked key or a stuck retry hits a ceiling, not a bill.
Endpoint URL
http://10.0.4.12/hook
Webhook URLs must use https.
Public https addresses only
Webhook URLs must use https, and deliveries to private or internal addresses are blocked — the test button cannot be used to probe your network.
https://erp-bridge.northlinesupply.com/goodshed
Send a test before you build
One click sends a webhook.test event to your URL, so you can see the headers and check your signature code before a real draft ever arrives.
CSV and Excel from every request
No middleware yet? Every request downloads as CSV or Excel, one row per line with the chosen item number, ready for the import your ERP already has.
Historical test · Lakeview RFQ
Runs the real pipeline on a past request.
Historical tests stay inside
Running past RFQs through Goodshed never reaches a webhook, so a test cannot create a quote in your ERP.
Against a custom integration, and against retyping.
Goodshed starts where a custom project would, without the project. It does not replace your ERP or write into it on its own.
| Goodshed | Custom ERP integration | Retyping | |
|---|---|---|---|
| Works from the export your ERP already produces | Built per system | ||
| Changes inside your ERP | None | Custom code or middleware | None |
| Catalog kept current from a scheduled export | |||
| Approved lines reach the ERP without retyping | Webhook or file | ||
| Creates the quote inside the ERP | Your bridge does, from the webhook | By hand | |
| Item numbers kept exactly — leading zeros, long numbers | Where typos happen | ||
| Signed deliveries that cannot be replayed | Depends on the build | Not applicable | |
| A person approves before anything leaves | Depends on the build |
Questions about integrations
Do you have a native connector to our ERP?
Which ERPs work?
Which columns does the catalog need?
What happens when we send the same catalog again?
How does an approved draft get into our ERP?
How do we verify a webhook?
What are the limits?
What if a key leaks?

The last quote your team retypes by hand.
Opens the early-access application with your email filled in. See our Privacy Policy.