Parse documents from a Pipedream workflow

ParseForMe connects to Pipedream in code: a Node.js step calls the API with fetch, and an HTTP source starts a workflow when a document is parsed. The key lives in a Pipedream environment variable, and the delivery signature is verified in the first step.

Drop a document to try it — freeNo signup to start. Get 30 free tokens when you sign in.

How ParseForMe fits a Pipedream workflow

Pipedream steps are Node.js, so no connector is involved: a code step posts to the documents endpoint with fetch and a bearer header. Triggers that hand you a file URL — email, storage, a form — map straight onto the JSON upload form.

Receiving parsed documents

Create a workflow with an HTTP / Webhook trigger and register its endpoint URL with the API. Each parsed document arrives with the extracted fields when you ask for them, and the first code step can verify the signature before anything else runs.

Keeping the key and the workflow safe

Store the key as a Pipedream environment variable and read it from process.env, so it is not part of the workflow definition you might share or fork. Verify the delivery signature and exit the flow when it does not match.

What this integration is not

There is no ParseForMe app in Pipedream’s registry, so you write the two calls yourself — the trade-off being that you also control retries, error handling and where the data goes next. Parsing spends tokens per page, whatever calls it.

How to set it up

  1. Create an API key

    Nothing else works without one. Keys are created in the ParseForMe dashboard — under Settings → API keys, or from the inline form on any in-app integration guide. The key is shown once, starts with pfm_live_ and is 52 characters long; store it in your automation platform’s credential store, never in a step’s visible fields. Owners and admins can create keys, a workspace can hold up to 10 active keys, and revoking one takes effect immediately. Send it as a bearer token and check it with /v1/me before you build anything else.

    curl -s https://api.parseforme.com/v1/me \
      -H "Authorization: Bearer pfm_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    
    # {"workspaceId":"…","workspaceName":"Acme","keyName":"Zapier","balance":420}
  2. Put the key in an environment variable

    Add the key under Settings → Environment Variables as PARSEFORME_API_KEY and read it in code as process.env.PARSEFORME_API_KEY. It stays out of the workflow’s exported definition, so sharing or forking the workflow does not share the key.

    export default defineComponent({
      async run({ steps, $ }) {
        const res = await fetch("https://api.parseforme.com/v1/me", {
          headers: { Authorization: `Bearer ${process.env.PARSEFORME_API_KEY}` },
        });
        return await res.json();
      },
    });
  3. Upload from a Node.js code step

    A code step with fetch is all you need. Post JSON with sourceUrl when the file already has an https URL — the common case in Pipedream, where the trigger usually hands you a link — and let ParseForMe fetch it. Pass an Idempotency-Key built from the trigger’s own id so a retried workflow does not parse the file twice.

    export default defineComponent({
      async run({ steps, $ }) {
        const res = await fetch("https://api.parseforme.com/v1/documents", {
          method: "POST",
          headers: {
            Authorization: `Bearer ${process.env.PARSEFORME_API_KEY}`,
            "Content-Type": "application/json",
            "Idempotency-Key": steps.trigger.event.id,
          },
          body: JSON.stringify({ sourceUrl: steps.trigger.event.fileUrl }),
        });
        if (!res.ok) throw new Error(`ParseForMe ${res.status}: ${await res.text()}`);
        return await res.json(); // { id, status: "uploaded", … }
      },
    });
  4. Trigger a workflow with an HTTP source

    Create a workflow with an HTTP / Webhook trigger, copy the endpoint URL Pipedream gives it, and register that URL with POST /v1/webhooks. Set includeResult so the parsed fields arrive with the event and the workflow starts with everything it needs. Registering an endpoint emails the workspace owners and admins.

    curl -s -X POST https://api.parseforme.com/v1/webhooks \
      -H "Authorization: Bearer pfm_live_…" \
      -H "Content-Type: application/json" \
      -d '{
            "url": "https://abcdef.m.pipedream.net",
            "events": ["document.parsed", "document.failed"],
            "includeResult": true
          }'
  5. Verify the signature in the first code step

    Pipedream gives a code step steps.trigger.event.body and .headers, so the check is a few lines: recompute the HMAC over t + "." + the raw body and compare it with the v1 value, rejecting anything more than 300 seconds old. Use $.flow.exit when it fails so the rest of the workflow never runs. Deliveries come from 46.62.162.196.

    const crypto = require('node:crypto');
    
    // Header: X-ParseForMe-Signature: t=<unix seconds>,v1=<hex hmac-sha256>
    // Signed payload is t + "." + the RAW request body (parse JSON afterwards).
    function verifyParseForMe(rawBody, header, secret) {
      const parts = header.split(',').map((p) => p.trim());
      const t = parts.find((p) => p.startsWith('t='))?.slice(2);
      // Two v1 entries appear while a secret is rotating — either may match.
      const sent = parts.filter((p) => p.startsWith('v1=')).map((p) => p.slice(3));
      if (!t || sent.length === 0) return false;
      if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // 5-minute tolerance
    
      const expected = crypto.createHmac('sha256', secret).update(t + '.' + rawBody).digest('hex');
      return sent.some(
        (sig) =>
          sig.length === expected.length &&
          crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)),
      );
    }
  6. Send a test event

    POST /v1/webhooks/{id}/test sends a synthetic document.parsed so the workflow has a real event to build against, and tells you the status your endpoint returned.

    curl -s -X POST https://api.parseforme.com/v1/webhooks/1f8c…/test \
      -H "Authorization: Bearer pfm_live_…"
    
    # {"delivered":true,"status":200}
  7. Keep a polling backstop

    Webhooks are the fast path, not the only one. GET /v1/documents returns documents newest first by creation time, each with a stable id, so a scheduled run picks up anything a delivery missed — and two things only polling sees: an event lost before it was queued (delivery is at-most-once), and a document whose event you missed while an endpoint was disabled. since= filters that same creation time — not the parse time — so set it a little before your last successful check and keep the window wide enough to cover a slow parse: a document created before your watermark but parsed after it will not reappear. Ask for up to 100 with limit=; the response is items only, with no cursor to follow. Deduplicate on the document id.

    GET /v1/documents?status=parsed&since=2026-09-02T08:00:00Z&limit=50
    Host: api.parseforme.com
    Authorization: Bearer pfm_live_…
    
    # → { "items": [ { "id": "8f14e45f-…", "status": "parsed", "kind": "invoice" } ] }
  8. Map the fields you care about

    GET /v1/document-types returns the nine document kinds and the fields each one extracts, so your mapping step can be built against the real field names instead of a guess. Parsed values arrive under result.fields, with a matching entry in result.confidence — route anything you want a human to check on that, rather than on a total.

    curl -s https://api.parseforme.com/v1/document-types \
      -H "Authorization: Bearer pfm_live_…"

Frequently asked questions

Is there a ParseForMe app in the Pipedream registry?

Not yet. A Node.js code step with fetch covers both directions: posting a document, and handling the webhook that carries the parsed fields.

Can a workflow send a file it only has a URL for?

Yes — that is the simplest path. Post JSON with a sourceUrl and ParseForMe fetches the https URL itself; multipart upload is there when you hold the bytes.

How do I stop a retried workflow parsing twice?

Send an Idempotency-Key header built from the trigger’s own event id. A repeat call returns the original document rather than parsing again.

How do I verify the webhook is really from ParseForMe?

Recompute the HMAC over the timestamp and the raw body in your first code step and compare it with the signature header, rejecting anything older than 300 seconds.

Pipedream is a trademark of Pipedream, Inc. ParseForMe is not affiliated with, endorsed by or sponsored by Pipedream.

Try it on your own document

Drop a document to see the structured data come back.

Drop a document to try it — freeNo signup to start. Get 30 free tokens when you sign in.