Manual Contribution Guide

July 18, 2026 ยท View on GitHub

This guide explains how to manually edit the database files. We recommend reading through this document first, then exploring the /data and /stores folders for reference examples. The authoritative source for every field is the JSON Schema in /schemas โ€” if anything here disagrees with a schema, the schema wins.

Note: Most contributors find the WebUI easier to use than manual editing โ€” the hosted instance at http://openfilamentdatabase.org/ needs no setup and opens a pull request for you. Consider using the WebUI unless you have a specific reason to edit files directly.

๐Ÿ“ Project Structure

The database is organized as a structured JSON-based hierarchy inside the /data directory, following this pattern:

data/
โ””โ”€โ”€ [brand-id]/                      # e.g. 22_network, prusament
    โ”œโ”€โ”€ brand.json
    โ”œโ”€โ”€ logo.(png|jpg|svg)           # exactly this filename
    โ””โ”€โ”€ [material-type]/             # e.g. PLA, ABS, PETG (from the material enum)
        โ”œโ”€โ”€ material.json
        โ””โ”€โ”€ [filament-id]/           # e.g. glow_pla
            โ”œโ”€โ”€ filament.json
            โ””โ”€โ”€ [variant-id]/        # e.g. luminous_blue
                โ”œโ”€โ”€ sizes.json
                โ””โ”€โ”€ variant.json

Stores live in a parallel tree:

stores/
โ””โ”€โ”€ [store-id]/                      # e.g. printed_solid
    โ”œโ”€โ”€ store.json
    โ””โ”€โ”€ logo.(png|jpg|svg)

๐Ÿงพ Naming Rules

Every folder name and every id field must match the regex ^[a-z0-9+]+(_[a-z0-9+]+)*$ โ€” lowercase letters, digits, and +, separated by single underscores. The id inside the JSON must match the folder name exactly.

Logos must be:

  • Named exactly logo.png, logo.jpg, or `logo.svg$ (\text{custom} \text{names} \text{are} \text{rejected} \text{by} \text{the} \text{validator})
  • \text{Square} (\text{width} = \text{height})
  • \text{Between} 100 \times 100 \text{and} 400 \times 400 \text{pixels} \text{for} \text{raster} \text{formats} (\text{PNG}/\text{JPG})
  • \text{A} \text{real} \text{SVG} (\text{root} \text{element} $`) for SVG files

๐Ÿ”‘ Canonical UUIDs โ€” leave them empty

Every entity (brand, material, filament, variant, and each spool in sizes.json) also has a canonical UUID: a stable, random uuid field that is not derived from the slug, so it never changes when a name or folder is renamed.

You never write this field by hand. When you add or edit data โ€” manually or via the WebUI โ€” simply omit uuid (or leave it empty). Continuous integration assigns a UUID to every new entry automatically when your pull request is merged. The id/folder-name slug is still what you set and what other files reference; the uuid is an additional identifier managed for you.

If you're working locally and want to preview or manage these IDs, use the ofd uuid commands (e.g. ofd uuid assign, ofd uuid find <uuid>).

Moved / former UUIDs (moved_from)

When data is merged or moved, one entity's uuid can be retired in favour of another's. To keep old references working, the surviving entity carries an optional moved_from array listing the former UUIDs that now resolve to it โ€” so a consumer that stored the old UUID can still find the current entity (ofd uuid find <old-uuid> redirects to it, and the build emits an api/v1/uuid-index.json redirect map for offline consumers).

You never write moved_from by hand either. It is managed by tooling: the merge_data / deduplicate_data scripts record a deleted entity's UUID onto its survivor automatically, and ofd uuid check enforces that every moved_from entry is a valid, unique UUID that doesn't collide with a live one.

๐Ÿท๏ธ Adding a Brand

  1. Create a folder under data/ named with the brand's id (lowercase snake_case, e.g. bambu_lab, prusament)

  2. Add the logo as logo.png, logo.jpg, or logo.svg (see naming rules above)

  3. Create brand.json:

    {
      "id": "bambu_lab",
      "name": "Bambu Lab",
      "website": "https://bambulab.com/",
      "logo": "logo.png",
      "origin": "CN"
    }
    

    Required fields:

    • id โ€” must match the folder name
    • name โ€” display name as shown by the manufacturer
    • website โ€” the manufacturer's official site
    • logo โ€” the logo filename (must be logo.png, logo.jpg, or logo.svg)
    • origin โ€” ISO 3166-1 alpha-2 country code (e.g. US, DE, CN), optionally with a region suffix like US-CA, or the literal string "Unknown". An empty string is not valid.

    Optional: source โ€” free-text note describing where the data came from (e.g. another database).

๐Ÿงช Adding a Material Type

  1. Inside the brand folder, create a folder named after the material type (e.g. PLA, PETG, ABS)

  2. Create material.json:

    { "material": "PLA" }
    

    Required:

    Optional:

    • material_class โ€” "FFF" (default) or "SLA"
    • default_max_dry_temperature โ€” integer, ยฐC
    • default_slicer_settings โ€” per-slicer profile names and overrides (see schemas/material_schema.json)

๐Ÿ“ฆ Adding a Filament

A filament represents a product line (e.g. "Silk PLA", "Glow PLA"), not a single colour.

  1. Inside the material folder, create a folder named with the filament id (e.g. glow_pla)

  2. Create filament.json:

    {
      "id": "glow_pla",
      "name": "Glow PLA",
      "diameter_tolerance": 0.02,
      "density": 1.24
    }
    

    Required:

    • id โ€” must match the folder name
    • name โ€” manufacturer's product-line name
    • diameter_tolerance โ€” number, mm (e.g. 0.02 for ยฑ0.02 mm)
    • density โ€” number, g/cmยณ (default 1.24)

    Optional (see schemas/filament_schema.json for the full list):

    • Mechanical: shore_hardness_a, shore_hardness_d, certifications (array of strings)
    • Drying / printing temperatures: max_dry_temperature, min_print_temperature, max_print_temperature, preheat_temperature, min_bed_temperature, max_bed_temperature, min_chamber_temperature, max_chamber_temperature, chamber_temperature
    • Hardware: min_nozzle_diameter
    • Documents: data_sheet_url, safety_sheet_url
    • Lifecycle: discontinued (boolean)
    • Slicer integration: slicer_ids (PrusaSlicer / BambuStudio / OrcaSlicer / Cura native IDs) and slicer_settings (per-slicer profile names, IDs, and temperature overrides)

๐ŸŽจ Adding a Variant

A variant represents a single colour or finish of a filament.

Inside the filament folder, create a folder named with the variant id (e.g. luminous_blue) and add two files:

variant.json

{
  "id": "luminous_blue",
  "name": "Luminous Blue",
  "color_hex": "#00BFFF",
  "traits": {
    "glow": true
  }
}

Required:

  • id โ€” must match the folder name
  • name โ€” the manufacturer's colour name
  • color_hex โ€” a #RRGGBB hex string, or an array of such strings for multi-colour filaments (gradient, dual-colour, etc.)

Optional (full list in schemas/variant_schema.json):

  • discontinued โ€” boolean
  • hex_variants โ€” array of alternative #RRGGBB codes the filament is known to report (e.g. via NFC)
  • color_standards โ€” object with any of ral, ncs, pantone, bs, munsell
  • traits โ€” an object mapping trait names to booleans, not an array of strings. Available traits include silk, matte, glow, translucent, transparent, recycled, biodegradable, abrasive, glitter, iridescent, temperature_color_change, gradual_color_change, coextruded, the contains_* family (carbon, glass, wood, metal, โ€ฆ), the imitates_* family, esd_safe, self_extinguishing, and many more. See the schema for the complete enumeration.

sizes.json

A JSON array of one or more size objects. At minimum:

[
  { "filament_weight": 1000, "diameter": 1.75 }
]

Required per entry:

  • filament_weight โ€” number, grams (default 1000)
  • diameter โ€” number, mm (typically 1.75 or 2.85)

Optional per entry:

  • Spool geometry: empty_spool_weight, spool_core_diameter, container_width, container_outer_diameter, container_hole_diameter
  • Identifiers: gtin (GTIN-12 or GTIN-13 โ€” preferred), ean (deprecated alias for gtin), article_number, barcode_identifier, nfc_identifier, qr_identifier
  • spool_refill โ€” boolean; mark the size as a refill for a reusable spool
  • discontinued โ€” boolean
  • purchase_links โ€” array of purchase entries:
    • store_id โ€” required; must match a folder under /stores
    • url โ€” required; product page URL
    • ships_from / ships_to โ€” optional per-link override (string or array of country codes)

Note: there is no is_affiliate field on purchase links. Affiliate handling is implicit in the store entry and the URL itself.

๐Ÿช Adding a Store

Stores are referenced by purchase_links[].store_id and live under /stores.

  1. Create a folder under /stores/ using the store id (e.g. printed_solid)

  2. Add the logo as logo.png, logo.jpg, or logo.svg (same rules as brand logos)

  3. Create store.json:

    {
      "id": "printed_solid",
      "name": "Printed Solid",
      "storefront_url": "https://www.printedsolid.com/",
      "logo": "logo.png",
      "ships_from": "US",
      "ships_to": ["US", "CA"]
    }
    

    Required:

    • id โ€” must match the folder name
    • name โ€” display name
    • storefront_url โ€” homepage URL
    • logo โ€” logo.png, logo.jpg, or logo.svg
    • ships_from โ€” country code string or array of country codes (e.g. "US" or ["US", "CA"]). Use an empty array [] if unknown.
    • ships_to โ€” same shape as ships_from. Use [] if unknown.

    Optional: source โ€” free-text data-source note.

    Note: there is no storefront_affiliate_link field in the store schema.

For full schema details, see schemas/store_schema.json.