Now in early access for electrical distributors.Apply
Skip to content

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.

items_nightly.csvemailed export
RFQ · ABC Electricforwarded mail

After a person approves

request.approved → HTTP 200

or download RQ-0142.xlsx

CSV and Excel catalog importEmailed item exportsCSV and Excel outNothing to install in your ERP

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.

Integrations

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 time

Goodshed 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.ai

Items 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.ai

Or 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

webhook.test

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 headers
Item #Item number *
DescDescription *
MfrManufacturer
Mfr PartManufacturer part number
SellPrice
On HandQuantity on hand
Import 12,840 rows

Your 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

ImportedEmailed · 12,840 rows · 12,831 written · 9 skippedToday 2:04 AM
ImportedEmailed · 12,835 rows · 12,826 written · 9 skippedYesterday 2:03 AM

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.

Approve draftRQ-0142 · approved by Dana

X-Goodshed-Event: request.approved

X-Goodshed-Timestamp: 1790086871

X-Goodshed-Signature: sha256=9f2c41e0b7…

request.approved → erp-bridge… (HTTP 200)

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/requestsSend 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/importThe 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/itemsItems 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 review
  • request.approvedDraft approved by a person
  • request.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…
ERP bridge · gs_live_Q3v9Revoke

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 testwebhook.test
Test event delivered (HTTP 200).

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.

CSVExcel

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.

Webhooksnever sent

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.

GoodshedCustom ERP integrationRetyping
Works from the export your ERP already producesBuilt per system
Changes inside your ERPNoneCustom code or middlewareNone
Catalog kept current from a scheduled export
Approved lines reach the ERP without retypingWebhook or file
Creates the quote inside the ERPYour bridge does, from the webhookBy hand
Item numbers kept exactly — leading zeros, long numbersWhere typos happen
Signed deliveries that cannot be replayedDepends on the buildNot applicable
A person approves before anything leavesDepends on the build

Questions about integrations

Do you have a native connector to our ERP?
No. Goodshed works from files your ERP already exports — uploaded, or emailed on a schedule — and from forwarded mail. For automation there is the API and webhooks, which your team or your integrator connects to the ERP. The system picker only changes the setup guidance.
Which ERPs work?
Any system that can produce an item export as CSV or Excel. Most can schedule a report and email it, which is the easiest path. If yours can only export by hand, upload the same file on the Catalog page.
Which columns does the catalog need?
Item number and description are required. Manufacturer, manufacturer part number, unit of measure, category, price and quantity on hand are used when present. Columns are guessed from the headers and confirmed by a person; over the API, a header the guess misses can be named in the query, e.g. ?sku=Item%20No.
What happens when we send the same catalog again?
Items are updated in place by item number: new items appear, changed prices and stock update, and nothing is duplicated. Rows without an item number or description are counted and skipped, never guessed at.
How does an approved draft get into our ERP?
Either the request.approved webhook sends the full draft to middleware you run, which creates the quote, or a rep downloads the draft as CSV or Excel and imports it. Goodshed does not write into your ERP itself.
How do we verify a webhook?
Compute HMAC-SHA256 over “<X-Goodshed-Timestamp>.<raw body>” with the webhook’s signing secret, prefix it with sha256=, and compare it to X-Goodshed-Signature. Reject any delivery whose timestamp is more than five minutes old.
What are the limits?
120 API requests and 30 catalog imports per workspace per hour, answered with 429 and Retry-After past that. A request can carry up to 8 files of 10 MB each; a catalog file can be up to 60 MB; the items endpoint takes up to 5,000 items per call.
What if a key leaks?
Revoke it on the Integrations page and make a new one. A key identifies the workspace and nothing more, it is stored only as a hash, and the hourly limits cap what it can be used for in the meantime. More on how data is kept in Security & data

The last quote your team retypes by hand.

Opens the early-access application with your email filled in. See our Privacy Policy.

NextSecurity & dataYour catalog and your customers stay yours, in U.S. data centers.