Getting Started
This guide walks you through authenticating with the Moloni ON API and making your first requests. The API uses GraphQL for all data operations, and supports two authentication methods:
- OAuth 2.0 (via API Clients): for web applications that act on behalf of a user
- API Keys (via API Keys): for machine-to-machine integrations with no user interaction
What is GraphQL?
GraphQL is a query language for APIs that lets you request exactly the data you need in a single request. Unlike REST, where each endpoint returns a fixed structure, GraphQL uses a single endpoint and you specify the fields you want in your query.
Key concepts:
- Queries read data (similar to GET in REST)
- Mutations create, update or delete data (similar to POST/PUT/DELETE)
- You choose which fields to return, avoiding over-fetching
If you're new to GraphQL, these resources are a good starting point:
- Introduction to GraphQL: Official GraphQL documentation
- GraphQL Queries and Mutations: How to read and write data
Prerequisites
- A Moloni ON account (sign up here)
- The API Access module enabled on your company (only required for company-related endpoints)
Choosing an authentication method
| OAuth 2.0 (API Client) | API Key | |
|---|---|---|
| Best for | Web apps acting on behalf of a user | Automated scripts, integrations, cron jobs |
| User interaction | Required (user must authorize via browser) | None (use the token directly) |
| Token format | Access token (1h) + refresh token (14d) | Permanent key (optional expiry) |
| Setup | Create API Client → OAuth flow → tokens | Create API Key → use immediately |
If you're building a web application where users log in and interact, use an API Client with the OAuth 2.0 flow.
If you need a server or script to access the API without user interaction (e.g. syncing data, automated reports), use an API Key.
Finding your company ID
Every query and mutation that operates on company data requires a companyId. This is the numeric ID of the company you want to work with.
Use the me query to fetch the authenticated user's profile and the list of companies they belong to:
query {
me {
errors { field msg }
data {
userId
name
email
userCompanies {
companyId
name
slug
isOwner
}
}
}
}
The userCompanies array lists every company the authenticated user has access to. Save the companyId of the company you want to work with; you will use it in every subsequent request.
If you need the full details of a specific company (country, fiscal settings, subscription plan, etc.), query it directly:
query {
company(companyId: 1) {
errors { field msg }
data {
companyId
name
slug
countryId
vat
}
}
}
Using the API
Regardless of the authentication method, all requests go to a single endpoint:
POST https://api.molonion.pt/v1
With the following headers:
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN
Where YOUR_TOKEN is either an OAuth access token or an API key.
Response Structure
All queries and mutations return a consistent shape:
{
"data": {
"queryName": {
"errors": [],
"data": { ... }
}
}
}
errors: an array of validation errors (empty on success). Each error has afieldandmsg.data: the result object (single item) or array (list endpoints).
List endpoints also include an options object with pagination info.
Example: Query Customers
query {
customers(companyId: 1) {
errors { field msg }
data {
customerId
name
vat
}
}
}
Example: Create a Customer
mutation {
customerCreate(companyId: 1, data: {
vat: "987654321"
number: "C002"
name: "Acme Lda"
countryId: 1
languageId: 1
}) {
errors { field msg }
data {
customerId
name
vat
number
}
}
}
Example: Delete
mutation {
customerDelete(companyId: 1, customerId: [42]) {
status
deletedCount
errors { field msg }
}
}
See the Deletion guide for details on bulk deletion, deletability checks, and document deletion rules.
Next steps
- API Clients: Set up OAuth 2.0 for web applications
- API Keys: Set up API keys for machine-to-machine integrations
- First Time Invoicing: Full setup walkthrough from document set to receipt
- Creating an Invoice: Create your first document
- Explorer: Try queries live in the GraphiQL interface