Graniite Docs
Quickstarts

n8n quickstart

Ingest URLs and text into Graniite from any n8n workflow in one HTTP Request node.

The typical n8n integration with Graniite is a single HTTP Request node. Most real automation shapes (Twilio webhooks, Google Drive triggers, Slack file events, podcast RSS firehoses) give you a URL pointing at content already hosted somewhere. Graniite fetches it on its end.

Step 1: mint a token

Visit https://graniite.co/settings/integrations and click + Create token. For an n8n automation:

  • Capabilities: check write so the integration can add content.
  • Access: pick Whole library if the automation should be able to file into different folders. Pick Specific folders with a single folder if everything from this automation should land in one bucket.
  • Auto-revoke after: 90 days is a sane default for an automation.

Copy the lev_… token string. You will only see it once.

Step 2: add an HTTP Request node

[Trigger fires with a URL]
        |
        v
[HTTP Request: POST /api/v1/ingest]
        |
        v
[Done, or wait + poll if you need to gate on transcription]

URL ingest

FieldValue
MethodPOST
URLhttps://graniite.co/api/v1/ingest
AuthenticationHeader Authorization: Bearer lev_…
Body typeJSON
BodySee below
{
  "kind": "url",
  "url": "{{ $json.url_from_upstream_trigger }}",
  "in_kb": true,
  "auto_transform": false
}

The URL can be a YouTube video, podcast episode page, RSS-direct .mp3, webpage article, or direct audio file URL (.mp3, .m4a, .wav, .ogg, .flac, or .aac). Graniite selects the appropriate pipeline.

Text ingest

For pasted text such as meeting notes, an email body, or anything that lives in your n8n flow as a string:

{
  "kind": "text",
  "title": "Notes from {{ $json.date }}",
  "text": "{{ $json.body }}",
  "in_kb": true,
  "auto_transform": false
}

text must be between 20 and 500,000 characters. title is required and limited to 500 characters.

Step 3: handle the response

The response shape is the same for both kinds:

{
  "status": "ready",
  "content_id": "uuid",
  "user_item_id": "uuid",
  "folder_id": "uuid or null"
}

status tells you whether you can move on or wait:

  • ready: webpage articles, pasted text, and YouTube videos already in the cache. The item is queryable now.
  • transcribing: YouTube videos not yet cached, podcast episodes, and direct audio URLs. The transcription pipeline runs in the background (10 to 60 seconds, longer for long media).

If you need to wait for transcription before the next node, add a Wait and HTTP Request loop:

[Wait 30s]
   |
   v
[HTTP: GET /api/v1/items/{{ user_item_id }}]
   |
   v
[IF: transcription_status === "done"]
   |
   +--- yes: Continue
   |
   +--- no: Loop back to Wait

Common recipes

Twilio call recording to Graniite

Twilio sends RecordingUrl on the webhook payload. Pipe it straight into ingest:

{ "kind": "url", "url": "{{ $json.RecordingUrl }}", "in_kb": true, "auto_transform": false }

Graniite fetches the recording, sends it to AssemblyAI for transcription, and embeds the result. End-to-end takes about 30 seconds for a short call.

Google Drive new file to Graniite

Use Drive's "shareable link" output from the trigger. If the file is audio or video, pass the URL. If it is a document, either share it publicly so Graniite can fetch it, or use the binary upload path. See the binary upload quickstart.

Slack message with file attachment to Graniite

Slack provides a url_private_download field with a short-lived token. Pass it directly:

{ "kind": "url", "url": "{{ $json.files[0].url_private_download }}" }

Slack's URL expires quickly. If your n8n run is delayed, the URL may return 401. For reliable replay, save the file to Drive first and ingest from there.

What about binary uploads?

If your trigger gives you binary file bytes instead of a URL (for example, reading from disk on a self-hosted n8n), use the three-step signed-URL flow. It is more nodes but unavoidable for client-side binaries.

On this page