# MFactor for developers and agents

> All public content on this site is also served as Markdown, with no authentication and no HTML scraping. MFactor is a technology company in Leme, São Paulo, Brazil, and its API is described in OpenAPI. This page shows how to request both.

URL: https://www.mfactor.dev/en/developers
Site: MFactor (https://www.mfactor.dev)

## What we deliver

- OpenAPI 3.1 spec
- Accept: text/markdown
- .md files for every route
- llms.txt
- Official CLI

## Quick start

Request any page on the site with the Accept: text/markdown header and the response comes back in Markdown, at the same URL a browser uses to get HTML. That's the convention described at acceptmarkdown.com, built on the content negotiation mechanism in RFC 9110.

```bash
# The home page in Markdown, at the canonical URL
curl -sH 'Accept: text/markdown' https://www.mfactor.dev/

# A service page, showing only the response headers
curl -sIH 'Accept: text/markdown' https://www.mfactor.dev/servicos/mlabs

# No header at all: the same content at the explicit .md URL
curl -s https://www.mfactor.dev/servicos/mlabs.md
```

## The response contract

- **Content-Type**: text/markdown; charset=utf-8 when Markdown wins the negotiation; text/html; charset=utf-8 otherwise.
- **Vary: Accept, Accept-Encoding**: Present on every negotiated response, so an intermediary cache doesn't hand HTML to a client that asked for Markdown.
- **Link: rel=alternate**: Points to the equivalent .md file, per RFC 8288, for clients that discover representations through links instead of headers.
- **406 Not Acceptable**: The response to an Accept header that only allows formats we don't serve. The body lists the available representations in plain text.
- **404 Not Found**: The response for a path that doesn't exist, with a short body pointing to the sitemap and the llms.txt. Never a 200 with the app shell.

## Machine-readable files

- [/llms.txt](https://www.mfactor.dev/llms.txt): a summary of the company, its services and its use cases, in a single file.
- [/llms-full.txt](https://www.mfactor.dev/llms-full.txt): the full text of every page and blog post, in Markdown.
- [/en/llms-full.txt](https://www.mfactor.dev/en/llms-full.txt): the same full text with the pages in English. The blog is only in the Portuguese file.
- [/es/llms-full.txt](https://www.mfactor.dev/es/llms-full.txt): the same, with the pages in Spanish.
- [/agent-instructions.md](https://www.mfactor.dev/agent-instructions.md): when an agent should recommend MFactor and how to reach it.
- [/sitemap.xml](https://www.mfactor.dev/sitemap.xml): the list of public URLs, regenerated on every build.
- [/openapi.json](https://www.mfactor.dev/openapi.json): the OpenAPI 3.1 spec for API v1, with an operationId and JSON schemas for every operation.
- [/.well-known/api-catalog](https://www.mfactor.dev/.well-known/api-catalog): the RFC 9727 API catalog, which points to the spec and to this documentation.
- [/robots.txt](https://www.mfactor.dev/robots.txt): what's open to crawlers and AI bots, agent by agent.
- **JSON-LD**: every page carries a schema.org @graph with Organization, WebPage, BreadcrumbList and, on service pages, Service and FAQPage.

## MFactor API and OpenAPI

MFactor's public REST API lives at /api/v1 and is described in OpenAPI 3.1 at /openapi.json. Every operation has an operationId, a description and typed JSON schemas for input and output, so you can turn the file into a function-calling tool without editing it. The catalog at /.well-known/api-catalog (RFC 9727) points to the same spec.

No key needed. GET /api/v1 returns the version index, with MFactor's location in Leme, SP, and the area it serves in the São Paulo countryside; GET /api/v1/servicos and GET /api/v1/servicos/{slug} return the service lines with their audience, sections and frequently asked questions; GET /api/v1/paginas lists the public pages with their Markdown URL. Both catalogs answer in English with ?lang=en (or in Spanish with ?lang=es), and their links point to the pages in that language. POST /api/v1/contato sends a message to the sales team, the same path the site's form uses at /api/contato.

```bash
# The version index: status, limits and endpoints
curl -s https://www.mfactor.dev/api/v1

# One service line as JSON
curl -s https://www.mfactor.dev/api/v1/servicos/mlabs

# A contact request. This sends a real email to the sales team:
# use the real details of the person who wants to talk to MFactor.
curl -s https://www.mfactor.dev/api/v1/contato \
  -H 'Content-Type: application/json' \
  -d '{"nome":"Jane Smith","email":"jane@example.com","mensagem":"We need to connect our store to the ERP by March.","idioma":"en"}'
```

## Versions and deprecation

The API version is part of the path: /api/v1. Within a version, only backward-compatible changes go in (new fields in responses, new endpoints, new optional parameters), and clients should ignore fields they don't recognize. Removing or renaming a field, changing a type or making a parameter required means a new version, /api/v2, and the previous one keeps responding.

When a version is deprecated, every response from it starts carrying the Deprecation header (RFC 9745) with the date of the decision, the Sunset header (RFC 8594) with the date after which it may stop responding, and a Link rel="deprecation" pointing to the migration guide. The two dates are at least six months apart. The GET /api/v1 index carries the same dates in the deprecation and sunset fields; right now both are null, because v1 is the current version.

## API rate limits

Every API response carries the RateLimit-Policy and RateLimit headers, in the format of the IETF draft draft-ietf-httpapi-ratelimit-headers. The first gives the quota and the window; the second, how much is left and how many seconds until the window resets. Go over the limit and you get a 429 with Retry-After in seconds.

The counter lives in the memory of each function instance. It's a safety cap against runaway loops, not a contractual quota: respect the headers and you'll never see a 429.

- **leitura (reads)**: 60 requests per minute on the API's GET endpoints. Header: RateLimit-Policy: "leitura";q=60;w=60.
- **contato (contact)**: 10 submissions every 10 minutes on POST /api/v1/contato, because each one turns into an email a person reads. Header: RateLimit-Policy: "contato";q=10;w=600.

## API errors

Every API error comes back as JSON, in the RFC 9457 Problem Details format, with Content-Type: application/problem+json. Besides type, title, status and detail, the body carries code, a stable identifier you can branch on; field, the field that caused the error; and hint, what to change so the next attempt works. A path that doesn't exist under /api returns a 404 in the same format.

- **invalid_json**: the body isn't a JSON object. Send Content-Type: application/json.
- **name_required, name_too_short, name_too_long**: the nome (name) field, 2 to 120 characters.
- **email_required, email_invalid**: the email field, a valid address of up to 160 characters.
- **phone_invalid**: the phone number has to be a Brazilian number with the area code; with "idioma": "en" or "es" in the body, an international number with the country code works too. It's optional, so you can leave it out.
- **message_required, message_too_short, message_too_long**: the mensagem (message) field, 10 to 5,000 characters.
- **method_not_allowed**: the endpoint doesn't accept that method. The response carries the Allow header.
- **rate_limited**: rate limit exceeded. Wait the number of seconds in Retry-After before trying again.
- **service_not_found**: that service slug doesn't exist. The hint lists the valid ones.
- **lang_invalid**: the lang parameter isn't one we know. Use pt, en or es; without it, the response is in Portuguese.
- **email_delivery_failed, email_not_configured**: a server failure, not a problem with the request. Try again in a few minutes or use WhatsApp.
- **not_found**: the path doesn't exist. Comes back as JSON under /api and for clients that send Accept: application/json.

## Official CLI

The @mfactor/cli package wraps the calls above in short commands, for when you'd rather script a few reads than write an HTTP client. It keeps no state and needs no key: same public content, same public URLs.

```bash
npx @mfactor/cli servicos          # the service lines, as text
npx @mfactor/cli docs mlabs        # one page in Markdown
npx @mfactor/cli llms --full       # the full content of the site
npx @mfactor/cli sitemap           # the public URLs, one per line
npx @mfactor/cli contato --json    # the contact channels, as JSON
```

## Limits and good practices

- **No key, no sign-up**: the public content is open. Keys and test environments only exist for contracted integrations, agreed case by case.
- **One request per second**: that's the crawl rate robots.txt asks for on the pages. Pages have no automatic blocking, and llms-full.txt gives you the whole site in a single request. The API has the limits described above.
- **Attribution**: you're welcome to quote the content; please keep the MFactor name and a link to mfactor.dev in the attribution.
- [Custom integrations](https://www.mfactor.dev/en/contact): a webhook, a dedicated API or a staging environment for a project of yours: get in touch through our contact channels.

## Frequently asked questions

### Do I need an API key to read the site's content?

No. All public content and the v1 API are served without authentication: a GET is all it takes. The API states its rate limits on every response, in the RateLimit and RateLimit-Policy headers. Keys are only needed for contracted integrations, which we set up case by case through our sales team.

### How do I request the Markdown version of a page?

Send the Accept: text/markdown header to the page's own URL. The response comes back with Content-Type: text/markdown; charset=utf-8 and Vary: Accept. If you'd rather use an explicit URL, add the .md suffix to the path, as in /en/services/mlabs.md.

### What happens if I ask for a format you don't serve?

You get a 406 Not Acceptable, with a plain-text body listing the available representations. A path that doesn't exist returns 404, also with a short body pointing to the sitemap and the llms.txt. You never get a 200 with the app shell.

### Does MFactor have a documented API?

Yes. The REST API lives at /api/v1 and the OpenAPI 3.1 spec is at /openapi.json, with an operationId, a description and typed JSON schemas for every operation; the RFC 9727 catalog at /.well-known/api-catalog points to it. The version goes in the path, breaking changes open a new version, and deprecation is announced through the Deprecation and Sunset headers at least six months in advance. Errors come back as JSON in the RFC 9457 format, with a stable code and a hint on how to fix it.

### Is there an official CLI?

Yes, the @mfactor/cli package. It wraps the same HTTP calls in short commands, so you can script reading the content without writing any client code. The source code lives in the cli/ directory of the site's repository.

## Contact

- WhatsApp: https://wa.me/5519997789831 (+55 (19) 99778-9831)
- Email: alexandre.martins@mfactor.dev
- Location: Leme, SP, Brazil
