Files
sure/docs/api/merchants.md
Blaž Dular 94422955f8 feat(merchants): add raw data import (csv) for merchants (#1992)
* feat(merchants): add csv import endpoint for merchants

* docs: update endpoint docs

* fix(merchant): recommended ai fixes
2026-06-06 16:33:32 +02:00

5.8 KiB

Merchants API

The Merchants API allows external applications to retrieve and bulk-import merchants within Sure. Merchants represent payees or vendors associated with transactions.

Generated OpenAPI specification

  • The source of truth for the documentation lives in spec/requests/api/v1/merchants_spec.rb. These specs authenticate against the Rails stack, exercise every merchant endpoint, and capture real response shapes.

  • Regenerate the OpenAPI document with:

    SWAGGER_DRY_RUN=0 bundle exec rspec spec/requests --format Rswag::Specs::SwaggerFormatter
    

    The task compiles the request specs and writes the result to docs/api/openapi.yaml.

  • Run just the documentation specs with:

    bundle exec rspec spec/requests/api/v1/merchants_spec.rb
    

Authentication requirements

Endpoint Required scope
GET endpoints read
POST /api/v1/merchants (CSV import) write

Available endpoints

Endpoint Scope Description
GET /api/v1/merchants read List all merchants available to the family.
GET /api/v1/merchants/{id} read Retrieve a single merchant by ID.
POST /api/v1/merchants write Bulk-import merchants from a CSV file.

Refer to the generated openapi.yaml for request/response schemas, reusable components, and security definitions.

Merchant types

Sure supports two types of merchants:

Type Description
FamilyMerchant Merchants created and owned by the family.
ProviderMerchant Merchants from external providers (e.g., Plaid) assigned to transactions.

The GET /api/v1/merchants endpoint returns both types: all family merchants plus any provider merchants that are assigned to the family's transactions.

Merchant object

A merchant response includes:

{
  "id": "uuid",
  "name": "Whole Foods",
  "type": "FamilyMerchant",
  "created_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-15T10:30:00Z"
}

Listing merchants

Example request:

GET /api/v1/merchants
Authorization: Bearer <access_token>

Example response:

[
  {
    "id": "550e8400-e29b-41d4-a716-446655440001",
    "name": "Amazon",
    "type": "FamilyMerchant",
    "created_at": "2024-01-10T08:00:00Z",
    "updated_at": "2024-01-10T08:00:00Z"
  },
  {
    "id": "550e8400-e29b-41d4-a716-446655440002",
    "name": "Starbucks",
    "type": "ProviderMerchant",
    "created_at": "2024-01-12T14:30:00Z",
    "updated_at": "2024-01-12T14:30:00Z"
  }
]

Using merchants with transactions

When creating or updating transactions, you can assign a merchant using the merchant_id field:

{
  "transaction": {
    "account_id": "uuid",
    "date": "2024-01-15",
    "amount": 75.50,
    "name": "Coffee",
    "nature": "expense",
    "merchant_id": "550e8400-e29b-41d4-a716-446655440002"
  }
}

Importing merchants via CSV

POST /api/v1/merchants accepts a multipart/form-data upload and bulk-creates FamilyMerchant records. Existing merchants with the same name are skipped (no update, no error).

Request

POST /api/v1/merchants
Content-Type: multipart/form-data
X-Api-Key: <write-scoped-key>

file=@merchants.csv

CSV format

Column Required Description
name Yes Merchant name. Rows with a blank name are skipped.
color No Hex colour code (e.g. #e99537). Defaults to a random palette colour.
website_url No Merchant website. Aliases accepted: website url, website.

The header row is required. Column names are matched case-insensitively and extra spaces, underscores, and asterisks are ignored (e.g. Name*, Website URL, and website_url all match).

Example CSV:

name,color,website_url
Coffee Shop,#e99537,https://coffeeshop.com
Pizza Palace,#4da568,https://pizzapalace.com
Bookstore,,

Response — 201 Created

{
  "imported": 2,
  "skipped": 1,
  "merchants": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440001",
      "name": "Coffee Shop",
      "type": "FamilyMerchant",
      "created_at": "2024-01-15T10:30:00Z",
      "updated_at": "2024-01-15T10:30:00Z"
    },
    {
      "id": "550e8400-e29b-41d4-a716-446655440002",
      "name": "Pizza Palace",
      "type": "FamilyMerchant",
      "created_at": "2024-01-15T10:30:00Z",
      "updated_at": "2024-01-15T10:30:00Z"
    }
  ]
}

skipped counts rows where a merchant with that name already exists for the family.

Error responses for CSV import

HTTP status error value Cause
401 unauthorized Missing or invalid API key.
403 forbidden API key lacks the write scope.
422 missing_file No file parameter supplied.
422 file_too_large File exceeds 10 MB.
422 invalid_file_type File is not a recognised CSV MIME type.
422 missing_column CSV has no name column.
422 invalid_csv CSV is malformed or cannot be parsed.

Importing merchants via the web UI

Merchants can also be imported through the built-in multi-step import flow at Settings → Imports → New Import → Raw Data → Import merchants. The flow supports the same CSV format as the API endpoint (upload → configure → clean → publish).

A shortcut button ("Import merchants") is also available directly on the Merchants page.

Error responses

Errors conform to the shared ErrorResponse schema in the OpenAPI document:

{
  "error": "Human readable error message"
}

Common error codes include unauthorized, not_found, and internal_server_error.