OpenMercantil User API
Authenticated Panel Pro endpoints — segments, lists, notes, tags, exports, audit. Requires session cookie (browser) and X-CSRF-Token header for mutations.
Authenticated Panel Pro endpoints — segments, lists, notes, tags, exports, audit. Requires session cookie (browser) and X-CSRF-Token header for mutations.
openapi: 3.1.0
info:
title: OpenMercantil User API
version: 1.9.3
summary: Versioned public-read, browser-account, billing, support and provider-callback contracts.
description: 'Public JSON API for Spanish company information derived from BORME and other public sources.
OpenMercantil is an independent informational service; it is NOT the BOE, BORME or Registro Mercantil
and does NOT replace official certificates or registry extracts.
**Rate limits.** Free: 60 req/min y 200 req/día por IP. Planes superiores (Profesional 5.000 req/día,
MAX 50.000 req/día, Enterprise 500.000+ req/día) según cuenta y API key. Cabeceras `X-RateLimit-Limit`,
`X-RateLimit-Remaining`, `X-RateLimit-Reset`, `X-OpenMercantil-Plan`, `Retry-After`.
**License and attribution.** Source-specific metadata in each response and the active versioned source
catalog prevails. OpenMercantil does not relicense upstream content under a blanket license. Unknown,
review and restricted datasets are omitted or return `503 legal_layer_unavailable`. BOE/BORME material
is re-used under Ley 37/2007 and its official version remains boe.es. Court judgments are not exposed;
CENDOJ remains citation-index only under CGPJ Reglamento 3/2010.
**Machine-readable catalog (DCAT-AP-ES):** https://openmercantil.es/catalog.rdf'
termsOfService: https://openmercantil.es/terminos-de-uso
contact:
name: OpenMercantil
url: https://openmercantil.es/soporte
email: social@openmercantil.es
license:
name: Source-specific upstream terms; see response catalog metadata
url: https://openmercantil.es/terminos-de-uso
x-publisher:
name: OpenMercantil
url: https://openmercantil.es/
email: social@openmercantil.es
x-spatial: http://publications.europa.eu/resource/authority/country/ESP
x-temporal: 2009-01-01/..
x-language: es
x-dcat-catalog: https://openmercantil.es/catalog.rdf
x-rate-limit:
free:
per_min: 60
per_day: 200
kind: anonymous-ip
profesional:
per_min: 120
per_day: 5000
kind: api-key
max:
per_min: 600
per_day: 50000
kind: api-key
enterprise:
per_min: 1200
per_day: 500000
kind: contract
x-methodology: https://openmercantil.es/metodologia
x-sources: https://openmercantil.es/fuentes
x-corrections: https://openmercantil.es/correcciones
x-contract-status: Public read, browser-account and provider-callback surfaces are explicitly separated
in this contract. Operator/admin routes are excluded. The public MCP consumes only the allowlisted
GET read plane.
x-account-segment-contract:
projection: company_public_v2 immutable corporate sidecar
synchronous_row_cap: 500
bounded_count_cap: 50001
count_semantics: The segment run response count is the number of rows returned, never a global total.
Dataset preview uses total_is_lower_bound=true and total_lower_bound when the bounded count reaches
50001.
related_web_dataset_surface:
preview_path: /mi-cuenta/datasets/preview
export_path: /mi-cuenta/datasets/export.csv
synchronous_export_max_rows: 500
overflow_status: 503
overflow_error: async_export_required
x-company-identity-contract:
version: '1.0'
projection: company_public_v2 immutable generation-bound corporate sidecar
applies_to: Every /api/v1/company/{slug}*, /api/v1/empresa/{slug}* and /api/v1/grafo/{slug} read before
any report, cache, graph or dataset lookup. /api/v1/companies/compare resolves both requested subjects
in one bounded company_public_v2 batch before either row is exposed; MCP company tools inherit these
preflights through REST.
resolution:
published: canonical corporate slug admitted
safe_alias: internally canonicalized and Content-Location emitted
withheld: neutral 404; includes absent, personal and ambiguous/quarantined identities
unavailable: 503 with no-store; clients must not infer absence
search: Exact corporate CIF, exact canonical/safe-alias slug, or bounded name_prefix2 pool scored
in application code. DNI/NIE and ambiguous CIFs return zero items.
public_company_count: company_public_projection_state.row_count
servers:
- url: https://openmercantil.es
description: Production
tags:
- name: User
description: Authenticated Panel Pro endpoints — segments, lists, notes, tags, exports, audit. Requires
session cookie (browser) and X-CSRF-Token header for mutations.
paths:
/api/v1/user/me:
get:
operationId: getUserMe
tags:
- User
summary: Current authenticated user
description: Returns id, email, name, plan (tier), persona_primary, onboarded flag and a fresh CSRF
token. Requires active session cookie.
security:
- cookieAuth: []
responses:
'200':
description: User profile
content:
application/json:
schema:
$ref: '#/components/schemas/UserMeResponse'
'401':
$ref: '#/components/responses/Unauthorized'
/api/v1/user/org:
get:
operationId: getUserOrganization
tags:
- User
summary: Get the current organization, seats and visible members
description: Returns null organization plus can_team when the user has no team. Non-privileged members
receive masked peer email addresses; the caller's own email is never masked. Invitation tokens
are never returned.
x-plane: account
security:
- cookieAuth: []
responses:
'200':
description: Organization context
content:
application/json:
schema:
$ref: '#/components/schemas/UserOrganizationResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'503':
description: Organization service unavailable
post:
operationId: createUserOrganization
tags:
- User
summary: Create an organization
description: MAX/Enterprise account mutation. The authenticated user becomes owner.
x-plane: account
security:
- cookieAuth: []
parameters:
- $ref: '#/components/parameters/CsrfHeader'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UserOrganizationNameRequest'
responses:
'201':
description: Organization created
content:
application/json:
schema:
$ref: '#/components/schemas/OrganizationCreatedResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
description: Plan does not include teams
'403':
description: Invalid CSRF
'409':
description: User already belongs to an organization
'503':
description: Organization service unavailable
put:
operationId: updateUserOrganization
tags:
- User
summary: Rename the current organization
description: Owner-only account mutation.
x-plane: account
security:
- cookieAuth: []
parameters:
- $ref: '#/components/parameters/CsrfHeader'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UserOrganizationNameRequest'
responses:
'200':
description: Organization renamed
content:
application/json:
schema:
$ref: '#/components/schemas/OkResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: Owner permission or CSRF required
'404':
$ref: '#/components/responses/NotFound'
'503':
description: Organization service unavailable
/api/v1/user/org/invites:
post:
operationId: createUserOrganizationInvite
tags:
- User
summary: Create or renew an organization invitation
description: Owner/admin mutation. Enforces seat and daily invitation limits, sends the invitation
out of band and never returns its token.
x-plane: account
security:
- cookieAuth: []
parameters:
- $ref: '#/components/parameters/CsrfHeader'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UserOrganizationInviteRequest'
responses:
'201':
description: Invitation created or renewed without token
content:
application/json:
schema:
$ref: '#/components/schemas/OrganizationInviteCreatedResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: Owner/admin permission or CSRF required
'409':
description: No organization, duplicate member or no free seat
'429':
$ref: '#/components/responses/TooManyRequests'
'503':
description: Organization or email service unavailable
/api/v1/user/org/invites/{id}/resend:
post:
operationId: resendUserOrganizationInvite
tags:
- User
summary: Rotate and resend an organization invitation
description: Owner/admin mutation with no request body. The rotated token is sent out of band and
never returned.
x-plane: account
security:
- cookieAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
minimum: 1
- $ref: '#/components/parameters/CsrfHeader'
responses:
'200':
description: Invitation resent
content:
application/json:
schema:
$ref: '#/components/schemas/EmailDispatchResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: Owner/admin permission or CSRF required
'404':
$ref: '#/components/responses/NotFound'
'409':
description: No free seat for an expired invitation
'429':
$ref: '#/components/responses/TooManyRequests'
'503':
description: Organization or email service unavailable
/api/v1/user/org/invites/{id}:
delete:
operationId: deleteUserOrganizationInvite
tags:
- User
summary: Cancel a pending organization invitation
description: Owner/admin mutation with no request body.
x-plane: account
security:
- cookieAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
minimum: 1
- $ref: '#/components/parameters/CsrfHeader'
responses:
'200':
description: Invitation cancelled
content:
application/json:
schema:
$ref: '#/components/schemas/OkResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: Owner/admin permission or CSRF required
'404':
$ref: '#/components/responses/NotFound'
'503':
description: Organization service unavailable
/api/v1/user/org/members/{id}:
put:
operationId: updateUserOrganizationMember
tags:
- User
summary: Change an organization member role
description: Owner-only mutation; the owner and caller's own role cannot be changed.
x-plane: account
security:
- cookieAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
minimum: 1
- $ref: '#/components/parameters/CsrfHeader'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UserOrganizationMemberRoleRequest'
responses:
'200':
description: Role updated
content:
application/json:
schema:
$ref: '#/components/schemas/OkResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: Owner permission or CSRF required
'404':
$ref: '#/components/responses/NotFound'
'503':
description: Organization service unavailable
delete:
operationId: deleteUserOrganizationMember
tags:
- User
summary: Remove a member from the organization
description: Owner/admin mutation with no request body. Admins may remove members but not owners
or other admins.
x-plane: account
security:
- cookieAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
minimum: 1
- $ref: '#/components/parameters/CsrfHeader'
responses:
'200':
description: Member removed
content:
application/json:
schema:
$ref: '#/components/schemas/OkResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: Owner/admin permission or CSRF required
'404':
$ref: '#/components/responses/NotFound'
'503':
description: Organization service unavailable
/api/v1/user/org/leave:
post:
operationId: leaveUserOrganization
tags:
- User
summary: Leave the current organization
description: Member/admin mutation with no request body. Owners must transfer ownership through
the supported process before leaving.
x-plane: account
security:
- cookieAuth: []
parameters:
- $ref: '#/components/parameters/CsrfHeader'
responses:
'200':
description: Organization left
content:
application/json:
schema:
$ref: '#/components/schemas/OkResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: Invalid CSRF
'404':
$ref: '#/components/responses/NotFound'
'409':
description: Owner cannot leave before transferring ownership
'503':
description: Organization service unavailable
/api/v1/user/persona:
get:
operationId: getUserPersona
tags:
- User
summary: Current persona config + available list
security:
- cookieAuth: []
responses:
'200':
description: Persona config
content:
application/json:
schema:
$ref: '#/components/schemas/UserPersonaResponse'
'401':
$ref: '#/components/responses/Unauthorized'
post:
operationId: postUserPersona
tags:
- User
summary: Set persona_primary
description: Set the account persona to a slug returned by the GET operation.
x-plane: account
security:
- cookieAuth: []
parameters:
- $ref: '#/components/parameters/CsrfHeader'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UserPersonaUpdateRequest'
responses:
'200':
description: Updated
content:
application/json:
schema:
$ref: '#/components/schemas/UserPersonaSelectedResponse'
'400':
description: Invalid persona slug
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: Invalid CSRF
/api/v1/user/segments:
get:
operationId: getUserSegments
x-query-contract:
allowed:
- pinned
- limit
unknown: 400 invalid_parameter
non_scalar: 400 invalid_parameter
lexical: 400 invalid_parameter
range: 422 validation_failed
tags:
- User
summary: List user segments
parameters:
- name: pinned
in: query
schema:
type: string
enum:
- '0'
- '1'
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 500
default: 200
security:
- cookieAuth: []
responses:
'200':
description: Segments list with tier_max
content:
application/json:
schema:
$ref: '#/components/schemas/UserSegmentListResponse'
'400':
$ref: '#/components/responses/BadRequest'
'422':
$ref: '#/components/responses/ValidationFailed'
'401':
$ref: '#/components/responses/Unauthorized'
post:
operationId: postUserSegments
tags:
- User
summary: Create segment
description: Create a tier-limited saved corporate segment. At least one effective UserSegmentFilters
anchor is required. Unknown fields and legacy forma/estado filters are rejected with 400; an individually
valid but unindexed combination is rejected with 422.
x-plane: account
security:
- cookieAuth: []
parameters:
- $ref: '#/components/parameters/CsrfHeader'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UserSegmentCreateRequest'
responses:
'201':
description: Created
content:
application/json:
schema:
$ref: '#/components/schemas/UserSegmentCreatedResponse'
'400':
description: Malformed/unknown filter, legally unavailable legacy filter, missing effective
anchor or tier limit
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'422':
description: Individually valid filters do not have a safe indexed combination
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'401':
$ref: '#/components/responses/Unauthorized'
/api/v1/user/segments/{id}:
get:
operationId: getUserSegmentsById
tags:
- User
summary: Get segment
security:
- cookieAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: Segment
content:
application/json:
schema:
$ref: '#/components/schemas/UserSegment'
'404':
$ref: '#/components/responses/NotFound'
put:
operationId: putUserSegmentsById
tags:
- User
summary: Replace mutable segment fields
description: When filters_json is present it follows UserSegmentFilters. Invalid or legacy fields
return 400; unsupported indexed combinations return 422.
x-plane: account
security:
- cookieAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
minimum: 1
- $ref: '#/components/parameters/CsrfHeader'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UserSegmentUpdateRequest'
responses:
'200':
description: Updated
content:
application/json:
schema:
$ref: '#/components/schemas/OkResponse'
'400':
description: Malformed/unknown or legally unavailable filter
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'422':
description: Individually valid filters do not have a safe indexed combination
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: Invalid CSRF
'404':
$ref: '#/components/responses/NotFound'
patch:
operationId: patchUserSegmentsById
tags:
- User
summary: Patch mutable segment fields
description: When filters_json is present it follows UserSegmentFilters. Invalid or legacy fields
return 400; unsupported indexed combinations return 422.
x-plane: account
security:
- cookieAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
minimum: 1
- $ref: '#/components/parameters/CsrfHeader'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UserSegmentUpdateRequest'
responses:
'200':
description: Updated
content:
application/json:
schema:
$ref: '#/components/schemas/OkResponse'
'400':
description: Malformed/unknown or legally unavailable filter
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'422':
description: Individually valid filters do not have a safe indexed combination
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: Invalid CSRF
'404':
$ref: '#/components/responses/NotFound'
delete:
operationId: deleteUserSegmentsById
tags:
- User
summary: Delete segment
x-plane: account
security:
- cookieAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
minimum: 1
- $ref: '#/components/parameters/CsrfHeader'
responses:
'200':
description: Deleted
content:
application/json:
schema:
$ref: '#/components/schemas/OkResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: Invalid CSRF
/api/v1/user/segments/{id}/pin:
post:
operationId: postUserSegmentsByIdPin
tags:
- User
summary: Toggle pin
description: This mutation has no request body.
x-plane: account
security:
- cookieAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
minimum: 1
- $ref: '#/components/parameters/CsrfHeader'
responses:
'200':
description: New pinned state
content:
application/json:
schema:
$ref: '#/components/schemas/UserSegmentPinResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: Invalid CSRF
'404':
$ref: '#/components/responses/NotFound'
/api/v1/user/segments/{id}/run:
post:
operationId: postUserSegmentsByIdRun
x-query-contract:
allowed:
- limit
unknown: 400 invalid_parameter
non_scalar: 400 invalid_parameter
lexical: 400 invalid_parameter
range: 422 validation_failed
tags:
- User
summary: Execute segment filters → companies
description: Resolves filters_json only against the immutable corporate company_public_v2 sidecar
and returns at most 500 rows. `count` is the number of rows in this bounded response, not an exact
global total. Invalid/legacy filters return 400, unsupported indexed combinations return 422,
and missing authority/sidecar or query failure returns 503. This mutation has no request body.
x-plane: account
security:
- cookieAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
minimum: 1
- name: limit
in: query
schema:
type: integer
minimum: 1
default: 100
maximum: 500
- $ref: '#/components/parameters/CsrfHeader'
responses:
'200':
description: Bounded result rows; count equals rows.length and is not a global total
content:
application/json:
schema:
$ref: '#/components/schemas/UserSegmentRunResponse'
'400':
description: Malformed/unknown or legally unavailable saved filter
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'422':
description: Saved filters are valid individually but lack a safe indexed combination
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: Invalid CSRF
'404':
$ref: '#/components/responses/NotFound'
'503':
description: Canonical authority, legal layer, immutable company sidecar or bounded query unavailable
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/api/v1/user/lists:
get:
operationId: getUserLists
tags:
- User
summary: List user lists
security:
- cookieAuth: []
responses:
'200':
description: Lists with tier_max
content:
application/json:
schema:
$ref: '#/components/schemas/UserListListResponse'
post:
operationId: postUserLists
tags:
- User
summary: Create list
x-plane: account
security:
- cookieAuth: []
parameters:
- $ref: '#/components/parameters/CsrfHeader'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UserListCreateRequest'
responses:
'201':
description: Created
content:
application/json:
schema:
$ref: '#/components/schemas/UserListCreatedResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: Invalid CSRF
/api/v1/user/lists/{id}:
get:
operationId: getUserListsById
tags:
- User
summary: Get list + items
security:
- cookieAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: List + items
content:
application/json:
schema:
$ref: '#/components/schemas/UserListDetail'
put:
operationId: putUserListsById
tags:
- User
summary: Replace mutable list fields
x-plane: account
security:
- cookieAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
minimum: 1
- $ref: '#/components/parameters/CsrfHeader'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UserListUpdateRequest'
responses:
'200':
description: Updated
content:
application/json:
schema:
$ref: '#/components/schemas/OkResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: Invalid CSRF
'404':
$ref: '#/components/responses/NotFound'
patch:
operationId: patchUserListsById
tags:
- User
summary: Patch mutable list fields
x-plane: account
security:
- cookieAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
minimum: 1
- $ref: '#/components/parameters/CsrfHeader'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UserListUpdateRequest'
responses:
'200':
description: Updated
content:
application/json:
schema:
$ref: '#/components/schemas/OkResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: Invalid CSRF
'404':
$ref: '#/components/responses/NotFound'
delete:
operationId: deleteUserListsById
tags:
- User
summary: Delete list and its items
description: This mutation has no request body.
x-plane: account
security:
- cookieAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
minimum: 1
- $ref: '#/components/parameters/CsrfHeader'
responses:
'200':
description: Deleted
content:
application/json:
schema:
$ref: '#/components/schemas/OkResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: Invalid CSRF
/api/v1/user/lists/{id}/items:
post:
operationId: postUserListsByIdItems
tags:
- User
summary: Add item to list
x-plane: account
security:
- cookieAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
minimum: 1
- $ref: '#/components/parameters/CsrfHeader'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UserListItemCreateRequest'
responses:
'201':
description: Added
content:
application/json:
schema:
$ref: '#/components/schemas/OkResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: Invalid CSRF
'404':
$ref: '#/components/responses/NotFound'
/api/v1/user/lists/{id}/items/{item_id}:
delete:
operationId: deleteUserListsByIdItemsByItemId
tags:
- User
summary: Remove item from list
description: This mutation has no request body.
x-plane: account
security:
- cookieAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
minimum: 1
- name: item_id
in: path
required: true
schema:
type: integer
minimum: 1
- $ref: '#/components/parameters/CsrfHeader'
responses:
'200':
description: Removed
content:
application/json:
schema:
$ref: '#/components/schemas/OkResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: Invalid CSRF
'404':
$ref: '#/components/responses/NotFound'
/api/v1/user/notes:
get:
operationId: getUserNotes
x-query-contract:
allowed:
- limit
unknown: 400 invalid_p
# --- truncated at 32 KB (130 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/openmercantil/refs/heads/main/openapi/openmercantil-user-api-openapi.yml