# hecigo Developer Resources

Everything hecigo publishes for developers and for automated clients, in one
place.

This page is the index; each entry below is a stable, predictable URL you can
bookmark or hard-code.

hecigo is an AI-native software lab: we build products and the connective layer
that lets systems, data and AI agents work together. Developers is one of three
pillars, next to Products and Solutions, and it is the one that is open source.

If you are an AI agent, start with
[hecigo.com/llms.txt](https://hecigo.com/llms.txt), which names what this site is
for and when to reach for us.

## Open-source n8n nodes by hecigo

hecigo maintains two n8n community nodes on npm. Both are MIT licensed, both are
used outside our own projects, and both take issues.

### n8n-nodes-zalo-platform

An n8n community node for the Zalo Bot Platform: send messages, photos and
stickers, and manage webhooks. Nine operations. Zalo is the dominant messaging
platform in Vietnam, and this node is what we use to reach it from an n8n
workflow.

- npm: https://www.npmjs.com/package/n8n-nodes-zalo-platform
- Source and README: https://github.com/hecigo/n8n-nodes-zalo-platform
- Install in n8n: **Settings → Community Nodes → Install →** `n8n-nodes-zalo-platform`
- Install from a shell: `npm install n8n-nodes-zalo-platform`
- Licence: MIT
- What breaks in production, written up honestly:
  [Running a Zalo bot on n8n: what breaks between test mode and production](https://hecigo.com/blog/n8n-zalo-bot-node-what-breaks-in-production/)

### n8n-nodes-firecrawl-v2

An n8n community node covering the Firecrawl v2 API (scrape, crawl, map,
search, extract, and the batch operations) against both Firecrawl Cloud and a
self-hosted instance. Ten operations, six of which return web content and are
not interchangeable.

- npm: https://www.npmjs.com/package/n8n-nodes-firecrawl-v2
- Source and README: https://github.com/hecigo/n8n-nodes-firecrawl-v2
- Install in n8n: **Settings → Community Nodes → Install →** `n8n-nodes-firecrawl-v2`
- Install from a shell: `npm install n8n-nodes-firecrawl-v2`
- Licence: MIT
- Which operation to reach for:
  [Firecrawl on n8n: picking the right operation, and what async does to your workflow](https://hecigo.com/blog/n8n-firecrawl-node-choosing-the-right-operation/)

All hecigo source lives under the GitHub organisation
[github.com/hecigo](https://github.com/hecigo). Issues and pull requests on either
node are the fastest route to a case being covered.

## Ads Insights over MCP

Ads Insights is a hosted, read-only MCP server for Meta Ads, Google Ads,
TikTok Ads, Google Analytics 4 and Google Search Console. The assistant calls an
Ads Insights tool, the tool reads each source's API directly, and the numbers come back in one shape.

- MCP endpoint: `https://ads.hecigo.com/mcp` (streamable HTTP)
- Server card: https://ads.hecigo.com/.well-known/mcp/server-card.json
- Sign-in: OAuth. The advertiser grants access on Meta's, Google's or TikTok's
  own consent screen and can revoke it there at any time.
- Tools: twelve, and every one declares `readOnlyHint`. Nothing in it creates,
  pauses, edits or funds an ad.

Paste the endpoint into the connector settings of Claude, ChatGPT, Cursor, VS
Code or any other MCP client. How authorization, revocation and customer data
work is on the [product page](https://hecigo.com/en/products/ads-insights/); the
sign-in and example prompts are at [ads.hecigo.com](https://ads.hecigo.com/).

## Machine-readable endpoints on hecigo.com

| Endpoint | Format | What it is |
| --- | --- | --- |
| [/llms.txt](https://hecigo.com/llms.txt) | Markdown, llmstxt.org | Site summary, when to use hecigo, and a link index |
| [/llms-full.txt](https://hecigo.com/llms-full.txt) | Markdown | Every public page and post concatenated, for a single fetch |
| [/agent-instructions.md](https://hecigo.com/agent-instructions.md) | Markdown | How an agent should call, cite, and hand off to hecigo |
| [/openapi.json](https://hecigo.com/openapi.json) | OpenAPI 3.1 | Every endpoint on this page, described formally |
| [/sitemap.xml](https://hecigo.com/sitemap.xml) | XML | Every indexable URL, with last-modified dates |
| [/robots.txt](https://hecigo.com/robots.txt) | Text | Crawl rules; no AI crawler is blocked |
| [/blog-index.json](https://hecigo.com/blog-index.json) | JSON | Blog metadata: slug, title, description, date, tags, language |

Everything in the table is public, unauthenticated, CORS-open, and free to fetch.
There is no rate limit beyond the CDN's own abuse protection. Please send a user
agent that identifies your client.

### The OpenAPI document

[/openapi.json](https://hecigo.com/openapi.json) describes this surface in
OpenAPI 3.1: every path above, the Accept negotiation on canonical page URLs,
the schema of `blog-index.json`, and the shape of the error bodies. It is
generated from the same manifest the negotiation runs on, so a retired post
cannot linger in it.

Read what it is carefully, because the word "API" invites the wrong assumption:
it documents a **read-only website surface** on hecigo.com, not a hosted hecigo
product. Every operation is a GET. There is no API key, no OAuth flow and no
write path behind it. The one hosted product, Ads Insights, lives on its own
domain, speaks MCP rather than REST, and is described by its own server card.

## The knowledge base

The site is also published as data, split into chunks an agent or a retrieval
index can load without scraping HTML:

- [/knowledge/index.jsonl](https://hecigo.com/knowledge/index.jsonl): one JSON
  object per line, one line per H2 section of every page, post and product, in
  both languages. Each object carries `id`, `url`, `locale`, `type`, `title`,
  `headingPath`, `text`, `entities` and `updated`.
- [/knowledge/entities.json](https://hecigo.com/knowledge/entities.json): the
  things those chunks talk about, the organisation and each product, with the
  ids the `entities` field points at, such as `org:hecigo` and
  `product:ads-insights`.

Ids are stable. They are built from the type, slug, locale and section anchor,
never from the text, so a corrected sentence updates a chunk in place instead of
creating a duplicate. A section longer than the chunk limit is split by
paragraph and gets a suffix: `~2`, `~3`. Where a page renders its headings as
anchors, `url` points at that section.

## Markdown content negotiation

Every public page on hecigo.com is available as Markdown at the same URL. Send
`Accept: text/markdown` and you get clean prose instead of a page of layout
markup, per the convention documented at
[acceptmarkdown.com](https://acceptmarkdown.com/):

```bash
curl -s -H "Accept: text/markdown" https://hecigo.com/
curl -s -H "Accept: text/markdown" https://hecigo.com/blog/n8n-zalo-bot-node-what-breaks-in-production/
```

Responses carry `Content-Type: text/markdown; charset=utf-8` and
`Vary: Accept, Accept-Encoding`. Q-values are honoured, so a browser's
`Accept: text/html,...,*/*;q=0.8` still gets HTML.

Every page also ships a `.md` sibling if you would rather address it directly,
advertised on the HTML response as
`Link: </index.md>; rel="alternate"; type="text/markdown"`. Vietnamese is the
default language: Vietnamese pages are `/<name>.md` and the homepage is
`/index.md`. English pages live under `/en/`: `/en/<name>.md`, with the English
homepage at `/en.md`.

```bash
curl -s https://hecigo.com/index.md
curl -s https://hecigo.com/developers.md
curl -s https://hecigo.com/en/developers.md
```

A request for a path that does not exist returns a real **HTTP 404**, and a
request whose `Accept` header we genuinely cannot satisfy returns **406 Not
Acceptable** rather than silently handing back the wrong format. Both answer in
a format you can parse; see [Errors](#errors) below.

## Errors

Errors on this site default to JSON, shaped per RFC 9457. You do not have to ask
for it:

```bash
curl -s https://hecigo.com/no-such-page/
curl -s https://hecigo.com/blog/no-such-post.md
```

```json
{
  "type": "https://hecigo.com/developers/#http-404",
  "title": "Not Found",
  "status": 404,
  "detail": "There is no page at /no-such-page/ on hecigo.com.",
  "instance": "/no-such-page/",
  "code": "not_found",
  "resolution": "Fetch https://hecigo.com/llms.txt for the link index or https://hecigo.com/sitemap.xml for every URL. ...",
  "documentation_url": "https://hecigo.com/developers/#errors",
  "links": { "agentIndex": "https://hecigo.com/llms.txt", "...": "..." }
}
```

Branch on `code`, not on the prose in `title` or `detail`. There are two values:
`not_found` and `representation_not_available`.

### Why JSON is the default

Because of what the wire actually tells us. A browser names `text/html` outright
in its `Accept` header, so a person who mistypes a URL still gets the rendered
404 page. `curl`, `fetch`, and every HTTP client library send `*/*` or no
`Accept` header at all, and the catch-all wildcard is not a claim to read HTML.
That is the whole distinction:

| What you send | What a 404 gives you |
| --- | --- |
| Nothing, or `*/*` | JSON problem document |
| `Accept: application/json` | JSON problem document |
| `Accept: text/markdown` | The Markdown recovery map |
| `Accept: text/html`, which is what a browser sends | The rendered 404 page |

This used to be the other way round: JSON arrived only if you named it, and
everything else got prose. An agent with a default header had no way to guess
which header would have worked.

### The status code is chosen on evidence

#### HTTP 404

There is no such path. The body names the path it is answering about, so an
agent fetching several dead URLs can tell the responses apart, and `resolution`
points at `/llms.txt` and `/sitemap.xml`.

#### HTTP 406

The path exists, but no representation matches your `Accept` header. The
`available` member names the two formats it does have. Calling this a 404 would
say the page is not there; calling a genuine 404 a 406 would say a dead path is
real and worth retrying in another format. The 406 body is JSON in every case,
including when you asked for something we have never heard of.

### Every error carries links in its headers

Including the HTML 404 page, which is how a client that cannot parse the page
still finds its way to this document:

```
Link: <https://hecigo.com/openapi.json>; rel="service-desc"; type="application/vnd.oai.openapi+json",
      <https://hecigo.com/llms.txt>; rel="service-doc"; type="text/plain",
      <https://hecigo.com/developers/#errors>; rel="help"; type="text/html"
```

Error responses carry `Vary: Accept, Accept-Encoding` and `Cache-Control: no-store`,
since the format of the body is chosen from a request header.

One limit worth knowing: paths that carry a non-document extension, such as
`/og-image.png`, are not routed through negotiation at all, so their errors are
the CDN's own. The document extensions `.md`, `.txt`, `.json` and `.xml` are.

## What hecigo does not publish

Stated plainly so nobody wastes an afternoon looking:

- **No product API on this domain.** `/openapi.json` describes this website's
  read-only surface, and nothing more. Integration engagements in the Solutions
  pillar run inside client infrastructure, so there is nothing multi-tenant to
  call there either. The one hosted product, Ads Insights, lives on
  `ads.hecigo.com` and speaks MCP, not REST.
- **No write endpoints on this domain.** Every operation documented here is a GET.
- **No webhooks you can subscribe to.** The only webhook on this domain is the
  enquiry-form intake, which is ours, not yours.

The public, usable surface is the two n8n nodes, the endpoints and knowledge base
above, and the Ads Insights MCP endpoint. When there is more, this page is where
it appears.

## Getting in touch about the code

- Bugs and feature requests on the nodes: open an issue on the relevant GitHub
  repository, with the payload that broke it.
- Everything else: hi@hecigo.com, or the form at
  [hecigo.com/en/contact](https://hecigo.com/en/contact/).
