RomM Sync API

The sync API from RomM — 5 operation(s) for sync.

Operations 5

POST /api/sync/negotiate Negotiate Sync #
POST /api/sync/sessions/{session_id}/complete Complete Sync Session #
GET /api/sync/sessions Get Sync Sessions #
GET /api/sync/sessions/{session_id} Get Sync Session #
POST /api/sync/devices/{device_id}/push-pull Trigger Push Pull #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/romm:romm-sync-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

romm-sync-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: RomM Sync API
  version: 5.4.0
tags:
- name: Sync
paths:
  /api/sync/negotiate:
    post:
      tags:
      - Sync
      summary: Negotiate Sync
      description: 'Negotiate sync operations between a client device and the server.


        The client sends its current save state, and the server returns a list of

        operations (upload, download, conflict, delete, no_op) to bring both sides

        in sync.


        A client that only holds part of the library can send `rom_ids` to scope the

        negotiation to the ROMs installed on the device, which keeps the response

        from listing downloads for ROMs it cannot play. The scope is read-only: a

        ROM left out is simply outside this negotiation, never a deletion signal.


        Saves are paired on (rom_id, slot). Clients that want a save to stay in sync

        should send a stable, non-null slot name (e.g. "autosave"). Null-slot saves

        are treated as archival, manual uploads: they are excluded from pairing, so a

        null-slot client save always negotiates as an "upload" even when an identical

        file (same content_hash) already exists on the server under a slot. This is

        intentional, since saves can be cloned across slots and null slots overlap

        with manual uploads.'
      operationId: negotiate_sync_api_sync_negotiate_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SyncNegotiatePayload'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SyncNegotiateResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      security:
      - OAuth2PasswordBearer:
        - assets.read
        - devices.read
      - HTTPBasic: []
  /api/sync/sessions/{session_id}/complete:
    post:
      tags:
      - Sync
      summary: Complete Sync Session
      description: Mark a sync session as completed, optionally ingesting play sessions.
      operationId: complete_sync_session_api_sync_sessions__session_id__complete_post
      security:
      - OAuth2PasswordBearer:
        - devices.write
      - HTTPBasic: []
      parameters:
      - name: session_id
        in: path
        required: true
        schema:
          type: integer
          title: Session Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SyncCompletePayload'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SyncCompleteResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/sync/sessions:
    get:
      tags:
      - Sync
      summary: Get Sync Sessions
      description: List sync sessions for the current user.
      operationId: get_sync_sessions_api_sync_sessions_get
      security:
      - OAuth2PasswordBearer:
        - devices.read
      - HTTPBasic: []
      parameters:
      - name: device_id
        in: query
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: Device Id
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          maximum: 10000
          minimum: 1
          description: Page size limit
          default: 50
          title: Limit
        description: Page size limit
      - name: offset
        in: query
        required: false
        schema:
          type: integer
          minimum: 0
          description: Page offset
          default: 0
          title: Offset
        description: Page offset
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/SyncSessionSchema'
                title: Response Get Sync Sessions Api Sync Sessions Get
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/sync/sessions/{session_id}:
    get:
      tags:
      - Sync
      summary: Get Sync Session
      description: Get a specific sync session.
      operationId: get_sync_session_api_sync_sessions__session_id__get
      security:
      - OAuth2PasswordBearer:
        - devices.read
      - HTTPBasic: []
      parameters:
      - name: session_id
        in: path
        required: true
        schema:
          type: integer
          title: Session Id
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SyncSessionSchema'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/sync/devices/{device_id}/push-pull:
    post:
      tags:
      - Sync
      summary: Trigger Push Pull
      description: Manually trigger a push-pull sync for a specific device.
      operationId: trigger_push_pull_api_sync_devices__device_id__push_pull_post
      security:
      - OAuth2PasswordBearer:
        - devices.write
      - HTTPBasic: []
      parameters:
      - name: device_id
        in: path
        required: true
        schema:
          type: string
          title: Device Id
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SyncSessionSchema'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
components:
  schemas:
    SyncPlaySessionEntry:
      properties:
        rom_id:
          anyOf:
          - type: integer
          - type: 'null'
          title: Rom Id
        save_slot:
          anyOf:
          - type: string
          - type: 'null'
          title: Save Slot
        start_time:
          type: string
          format: date-time
          title: Start Time
        end_time:
          type: string
          format: date-time
          title: End Time
        duration_ms:
          type: integer
          minimum: 0.0
          title: Duration Ms
      type: object
      required:
      - start_time
      - end_time
      - duration_ms
      title: SyncPlaySessionEntry
    PlaySessionIngestResponse:
      properties:
        results:
          items:
            $ref: '#/components/schemas/PlaySessionIngestResult'
          type: array
          title: Results
        created_count:
          type: integer
          title: Created Count
        skipped_count:
          type: integer
          title: Skipped Count
      type: object
      required:
      - results
      - created_count
      - skipped_count
      title: PlaySessionIngestResponse
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
            - type: string
            - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
        input:
          title: Input
        ctx:
          type: object
          title: Context
      type: object
      required:
      - loc
      - msg
      - type
      title: ValidationError
    SyncCompletePayload:
      properties:
        operations_completed:
          type: integer
          title: Operations Completed
          default: 0
        operations_failed:
          type: integer
          title: Operations Failed
          default: 0
        play_sessions:
          anyOf:
          - items:
              $ref: '#/components/schemas/SyncPlaySessionEntry'
            type: array
          - type: 'null'
          title: Play Sessions
      type: object
      title: SyncCompletePayload
    SyncOperationSchema:
      properties:
        action:
          type: string
          enum:
          - upload
          - download
          - conflict
          - no_op
          - delete
          title: Action
          description: Operation the client should perform. 'upload' when the client has a save the server lacks (including any null-slot save, which is never paired with server saves), 'download' when the server has a newer or unknown save or the client holds a version removed here, 'conflict' when both sides changed independently, 'delete' when the slot the client still holds was emptied here, and 'no_op' when no action is needed.
        rom_id:
          type: integer
          title: Rom Id
          description: ID of the ROM this operation applies to.
        save_id:
          anyOf:
          - type: integer
          - type: 'null'
          title: Save Id
          description: ID of the server save, if one exists (null for uploads and deletes).
        file_name:
          type: string
          title: File Name
          description: Name of the save file.
        slot:
          anyOf:
          - type: string
          - type: 'null'
          title: Slot
          description: Slot the operation applies to. Echoes the client slot for uploads and deletes; for downloads and conflicts it is the server save's slot.
        emulator:
          anyOf:
          - type: string
          - type: 'null'
          title: Emulator
          description: Emulator associated with the save, if known.
        reason:
          type: string
          title: Reason
          description: Human-readable explanation of why this operation was chosen.
        server_updated_at:
          anyOf:
          - type: string
          - type: 'null'
          title: Server Updated At
          description: Last-modified timestamp of the server save, when applicable.
        server_content_hash:
          anyOf:
          - type: string
          - type: 'null'
          title: Server Content Hash
          description: Content hash of the server save, when applicable.
      type: object
      required:
      - action
      - rom_id
      - file_name
      - reason
      title: SyncOperationSchema
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    SyncNegotiateResponse:
      properties:
        session_id:
          type: integer
          title: Session Id
        operations:
          items:
            $ref: '#/components/schemas/SyncOperationSchema'
          type: array
          title: Operations
        total_upload:
          type: integer
          title: Total Upload
        total_download:
          type: integer
          title: Total Download
        total_conflict:
          type: integer
          title: Total Conflict
        total_no_op:
          type: integer
          title: Total No Op
        total_delete:
          type: integer
          title: Total Delete
      type: object
      required:
      - session_id
      - operations
      - total_upload
      - total_download
      - total_conflict
      - total_no_op
      - total_delete
      title: SyncNegotiateResponse
    SyncSessionSchema:
      properties:
        id:
          type: integer
          title: Id
        device_id:
          type: string
          title: Device Id
        user_id:
          type: integer
          title: User Id
        status:
          type: string
          title: Status
        initiated_at:
          type: string
          title: Initiated At
        completed_at:
          anyOf:
          - type: string
          - type: 'null'
          title: Completed At
        operations_planned:
          type: integer
          title: Operations Planned
        operations_completed:
          type: integer
          title: Operations Completed
        operations_failed:
          type: integer
          title: Operations Failed
        error_message:
          anyOf:
          - type: string
          - type: 'null'
          title: Error Message
        created_at:
          type: string
          title: Created At
        updated_at:
          type: string
          title: Updated At
      type: object
      required:
      - id
      - device_id
      - user_id
      - status
      - initiated_at
      - operations_planned
      - operations_completed
      - operations_failed
      - created_at
      - updated_at
      title: SyncSessionSchema
    ClientSaveState:
      properties:
        rom_id:
          type: integer
          title: Rom Id
          description: ID of the ROM this save belongs to.
        file_name:
          type: string
          title: File Name
          description: Name of the save file on the client.
        slot:
          anyOf:
          - type: string
          - type: 'null'
          title: Slot
          description: 'Save slot name. Saves are paired between client and server on (rom_id, slot), so provide a stable slot name (e.g. ''autosave'') to keep a save in sync across negotiations. A null slot is treated as an archival, manual-upload save: it is never paired with slotted server saves, so a null-slot client save always negotiates as an ''upload'' even when an identical file already exists on the server under a slot.'
        emulator:
          anyOf:
          - type: string
          - type: 'null'
          title: Emulator
          description: Emulator that produced the save, if known.
        content_hash:
          anyOf:
          - type: string
          - type: 'null'
          title: Content Hash
          description: Hash of the save contents, used to detect identical saves.
        updated_at:
          type: string
          format: date-time
          title: Updated At
          description: Last-modified timestamp of the save on the client.
        file_size_bytes:
          type: integer
          title: File Size Bytes
          description: Size of the save file in bytes.
      type: object
      required:
      - rom_id
      - file_name
      - updated_at
      - file_size_bytes
      title: ClientSaveState
    PlaySessionIngestResult:
      properties:
        index:
          type: integer
          title: Index
        status:
          type: string
          enum:
          - created
          - duplicate
          - error
          title: Status
        id:
          anyOf:
          - type: integer
          - type: 'null'
          title: Id
        detail:
          anyOf:
          - type: string
          - type: 'null'
          title: Detail
      type: object
      required:
      - index
      - status
      title: PlaySessionIngestResult
    SyncCompleteResponse:
      properties:
        session:
          $ref: '#/components/schemas/SyncSessionSchema'
        play_session_ingest:
          anyOf:
          - $ref: '#/components/schemas/PlaySessionIngestResponse'
          - type: 'null'
      type: object
      required:
      - session
      title: SyncCompleteResponse
    SyncNegotiatePayload:
      properties:
        device_id:
          anyOf:
          - type: string
          - type: 'null'
          title: Device Id
          description: ID of the syncing device. Optional when the request uses a device-bound client token, in which case the device is inferred from the token.
        saves:
          items:
            $ref: '#/components/schemas/ClientSaveState'
          type: array
          title: Saves
          description: Current save state on the client.
        rom_ids:
          anyOf:
          - items:
              type: integer
              exclusiveMinimum: 0.0
            type: array
            maxItems: 500
          - type: 'null'
          title: Rom Ids
          description: 'IDs of the ROMs installed on the device. When provided, downloads are offered only for these ROMs (plus any ROM the client sent a save for) instead of the user''s whole save library. This is a read-only scope: omitting a ROM never deletes or unlinks its saves. At most 500 IDs per request.'
        restore_unlisted:
          type: boolean
          title: Restore Unlisted
          description: Offer every current server save the client did not list as a download, even one this device already synced. For clients that never delete saves themselves (such as a browser, whose storage can be evicted), so a missing save means lost rather than deleted.
          default: false
        emulators:
          anyOf:
          - items:
              type: string
            type: array
          - type: 'null'
          title: Emulators
          description: Emulators whose saves this client can load. When provided, only server saves written by one of them are paired or offered, so a save from another emulator in the same slot is left alone.
      type: object
      required:
      - saves
      title: SyncNegotiatePayload
  securitySchemes:
    OAuth2PasswordBearer:
      type: oauth2
      flows:
        password:
          scopes:
            me.read: View your profile
            roms.read: View ROMs
            platforms.read: View platforms
            assets.read: View assets
            devices.read: View devices
            firmware.read: View firmware
            roms.user.read: View user-rom properties
            collections.read: View collections
            playlists.read: View music playlists
            me.write: Modify your profile
            assets.write: Modify assets
            devices.write: Modify devices
            roms.user.write: Modify user-rom properties
            collections.write: Modify collections
            playlists.write: Modify music playlists
            roms.write: Modify ROMs
            platforms.write: Modify platforms
            firmware.write: Modify firmware
            users.read: View users
            users.write: Modify users
            tasks.run: Run tasks
            logs.read: View backend logs
          tokenUrl: /token
    HTTPBasic:
      type: http
      scheme: basic