{
  "info": {
    "description": "The FuzzyMatch API lets you submit record-matching jobs and read the results from your own code.\n\n## Authentication\nCreate an API key in your account settings and send it with every request:\n\n    Authorization: Bearer fm_live_...\n\n(`X-API-Key: fm_live_...` also works.) Keys are shown once and stored only as a hash.\nKeys in the query string are refused.\n\n## Rate limits\nEach plan has a daily API call allowance (`api_calls_per_day`), counted over a fixed 24-hour\nwindow that starts with your first call.\nThe allowance is shared by all keys on an account, and a key can be given a lower limit of its own.\nEvery response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (unix time);\ngoing over returns `429` with `Retry-After`.\n\nPlan limits on jobs (rows per file, jobs per day, rows per month) return `402` with\n`code: PLAN_LIMIT_EXCEEDED` and a `reason`.\n\n## Idempotency\nSend an `Idempotency-Key` header (any unique string up to 255 characters) on `POST` requests.\nRetrying with the same key and body within 24 hours returns the first response with\n`Idempotent-Replayed: true` instead of doing the work again. Reusing a key with a different\nbody returns `422 IDEMPOTENCY_KEY_REUSED`; a retry while the first request is still running\nreturns `409 IDEMPOTENCY_IN_PROGRESS`. Errors and 5xx responses are not stored.\n\n## Webhooks\nRegister an HTTPS endpoint to receive `job.completed` and `job.failed` events. Each delivery is a\n`POST` with a JSON body `{\"id\", \"type\", \"created\", \"api_version\", \"data\"}` and these headers:\n\n- `FuzzyMatch-Signature: t=<unix seconds>,v1=<hex>` where\n  `v1 = HMAC-SHA256(key = endpoint secret, message = \"<t>.\" + raw request body)`\n- `FuzzyMatch-Event`: the event type\n- `FuzzyMatch-Webhook-Id`: the event id, the same on every retry (use it to drop duplicates)\n\nTo verify: recompute `v1` over the raw body bytes exactly as received, compare in constant time,\nand reject the delivery if `t` is more than 5 minutes from your clock (this stops replays).\nRespond with any 2xx within 10 seconds. Network errors, timeouts, 408, 425, 429 and 5xx are\nretried with exponential backoff (8 attempts over about an hour); other 4xx responses are not\nretried. An endpoint is disabled after 10 events in a row fail; re-enable it with\n`PATCH /webhooks/{id}` and `active: true`.\n\n## Errors\nErrors are JSON: `{\"error\": \"...\", \"code\": \"MACHINE_CODE\", \"message\": \"...\"}`.",
    "contact": {
      "name": "FuzzyMatch Support",
      "email": "support@fuzzymatcher.com"
    },
    "title": "FuzzyMatch API",
    "version": "v1"
  },
  "servers": [
    {
      "url": "https://app.fuzzymatcher.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "BearerAuth": []
    },
    {
      "ApiKeyHeader": []
    }
  ],
  "paths": {
    "/api/v1/jobs": {
      "post": {
        "responses": {
          "422": {
            "$ref": "#/components/responses/UNPROCESSABLE_ENTITY"
          },
          "409": {
            "description": "Idempotency-Key in use by a request still running.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "A plan limit would be exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "202": {
            "description": "Accepted",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed per day",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left today",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the daily window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JobCreate"
              }
            }
          }
        },
        "summary": "Submit a match job",
        "description": "Matching runs in the background. Poll `GET /jobs/{job_id}` (or subscribe to\nthe `job.completed` webhook), then read `GET /jobs/{job_id}/results`.\nRows submitted count toward the plan's monthly row allowance.",
        "tags": [
          "Jobs"
        ],
        "parameters": [
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            },
            "description": "Any unique string (a UUID works well). Retrying with the same key and body within 24 hours returns the original response instead of doing the work twice."
          }
        ],
        "operationId": "createJob"
      },
      "get": {
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 500
            },
            "required": false
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            },
            "required": false
          }
        ],
        "responses": {
          "422": {
            "$ref": "#/components/responses/UNPROCESSABLE_ENTITY"
          },
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed per day",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left today",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the daily window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobList"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "List jobs, newest first",
        "tags": [
          "Jobs"
        ],
        "operationId": "listJobs"
      }
    },
    "/api/v1/jobs/{job_id}": {
      "get": {
        "responses": {
          "404": {
            "description": "No such job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed per day",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left today",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the daily window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "Get a job's status",
        "tags": [
          "Jobs"
        ],
        "operationId": "getJob"
      },
      "parameters": [
        {
          "in": "path",
          "name": "job_id",
          "required": true,
          "schema": {
            "type": "string",
            "minLength": 1
          }
        }
      ]
    },
    "/api/v1/jobs/{job_id}/results": {
      "get": {
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "default": 100,
              "minimum": 1,
              "maximum": 1000
            },
            "required": false
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            },
            "required": false
          },
          {
            "in": "query",
            "name": "include_rows",
            "description": "Include the full source and target rows with each match.",
            "schema": {
              "type": "boolean",
              "default": true
            },
            "required": false
          }
        ],
        "responses": {
          "422": {
            "$ref": "#/components/responses/UNPROCESSABLE_ENTITY"
          },
          "409": {
            "description": "The job hasn't completed yet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed per day",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left today",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the daily window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobResults"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "Get a completed job's matches",
        "tags": [
          "Jobs"
        ],
        "operationId": "getJobResults"
      },
      "parameters": [
        {
          "in": "path",
          "name": "job_id",
          "required": true,
          "schema": {
            "type": "string",
            "minLength": 1
          }
        }
      ]
    },
    "/api/v1/webhooks": {
      "get": {
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed per day",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left today",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the daily window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookList"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "List webhook endpoints",
        "tags": [
          "Webhooks"
        ],
        "operationId": "listWebhooks"
      },
      "post": {
        "responses": {
          "422": {
            "$ref": "#/components/responses/UNPROCESSABLE_ENTITY"
          },
          "409": {
            "description": "Endpoint limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "201": {
            "description": "Created",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed per day",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left today",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the daily window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookWithSecret"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreate"
              }
            }
          }
        },
        "summary": "Create a webhook endpoint",
        "description": "The response includes the signing `secret`. It is shown only here; store it\nto verify the `FuzzyMatch-Signature` header on deliveries. An idempotent\nreplay of this request returns the endpoint without the secret; if the\noriginal response was lost, delete the endpoint and create a new one.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            },
            "description": "Any unique string (a UUID works well). Retrying with the same key and body within 24 hours returns the original response instead of doing the work twice."
          }
        ],
        "operationId": "createWebhook"
      }
    },
    "/api/v1/webhooks/{webhook_id}": {
      "get": {
        "responses": {
          "404": {
            "description": "No such endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed per day",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left today",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the daily window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "Get a webhook endpoint",
        "tags": [
          "Webhooks"
        ],
        "operationId": "getWebhook"
      },
      "parameters": [
        {
          "in": "path",
          "name": "webhook_id",
          "required": true,
          "schema": {
            "type": "string",
            "minLength": 1
          }
        }
      ],
      "patch": {
        "responses": {
          "422": {
            "$ref": "#/components/responses/UNPROCESSABLE_ENTITY"
          },
          "404": {
            "description": "No such endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed per day",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left today",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the daily window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookUpdate"
              }
            }
          }
        },
        "summary": "Update a webhook endpoint",
        "description": "Setting `active` to true re-enables an endpoint that was switched off after\nrepeated failed deliveries, and resets its failure count.",
        "tags": [
          "Webhooks"
        ],
        "operationId": "updateWebhook"
      },
      "delete": {
        "responses": {
          "404": {
            "description": "No such endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "204": {
            "description": "No Content",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed per day",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left today",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the daily window resets",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "Delete a webhook endpoint",
        "tags": [
          "Webhooks"
        ],
        "operationId": "deleteWebhook"
      }
    },
    "/api/v1/webhooks/{webhook_id}/test": {
      "post": {
        "responses": {
          "409": {
            "description": "The endpoint is disabled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "202": {
            "description": "Accepted",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed per day",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left today",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the daily window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookTest"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "Send a test event",
        "description": "Queues a signed `webhook.test` event to the endpoint, whatever events it is\nsubscribed to.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            },
            "description": "Any unique string (a UUID works well). Retrying with the same key and body within 24 hours returns the original response instead of doing the work twice."
          }
        ],
        "operationId": "testWebhook"
      },
      "parameters": [
        {
          "in": "path",
          "name": "webhook_id",
          "required": true,
          "schema": {
            "type": "string",
            "minLength": 1
          }
        }
      ]
    },
    "/api/v1/account": {
      "get": {
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed per day",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left today",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the daily window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "Get account, plan limits and today's API usage",
        "tags": [
          "Account"
        ],
        "operationId": "getAccount"
      }
    }
  },
  "tags": [
    {
      "name": "Jobs",
      "description": "Submit match jobs and read their results."
    },
    {
      "name": "Webhooks",
      "description": "Get notified when jobs finish. Deliveries are signed; see the API description for how to verify them."
    },
    {
      "name": "Account",
      "description": "The account, plan and limits behind the API key."
    }
  ],
  "openapi": "3.0.3",
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable message"
          },
          "code": {
            "type": "string",
            "description": "Stable machine-readable error code"
          },
          "message": {
            "type": "string",
            "description": "Same as error (kept for client compatibility)"
          },
          "errors": {
            "type": "object",
            "description": "Field errors, for VALIDATION_ERROR",
            "additionalProperties": {}
          },
          "reason": {
            "type": "string",
            "description": "Which limit was hit, for PLAN_LIMIT_EXCEEDED"
          },
          "limit": {
            "type": "integer",
            "description": "The limit that applies, when relevant",
            "nullable": true
          },
          "plan": {
            "type": "string",
            "description": "The caller's plan, when relevant"
          },
          "request_id": {
            "type": "string",
            "description": "Quote this when contacting support"
          }
        },
        "required": [
          "code",
          "error"
        ],
        "additionalProperties": false
      },
      "PaginationMetadata": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer",
            "description": "Total number of items."
          },
          "total_pages": {
            "type": "integer",
            "description": "Total number of pages."
          },
          "first_page": {
            "type": "integer",
            "description": "First available page number."
          },
          "last_page": {
            "type": "integer",
            "description": "Last available page number."
          },
          "page": {
            "type": "integer",
            "description": "Current page number."
          },
          "previous_page": {
            "type": "integer",
            "description": "Previous page number."
          },
          "next_page": {
            "type": "integer",
            "description": "Next page number."
          }
        },
        "additionalProperties": false
      },
      "MatchConfiguration": {
        "type": "object",
        "properties": {
          "field_mapping": {
            "type": "object",
            "description": "Source column -> target column. Omit to let FuzzyMatch infer it.",
            "additionalProperties": {
              "type": "string"
            }
          },
          "field_weights": {
            "type": "object",
            "description": "Column -> weight between 0 and 1. Omit to infer.",
            "additionalProperties": {
              "type": "number",
              "minimum": 0.0,
              "maximum": 1.0
            }
          },
          "field_types": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "enum": [
                "text",
                "name",
                "email",
                "phone",
                "address",
                "number",
                "date"
              ]
            }
          },
          "min_threshold": {
            "type": "number",
            "minimum": 0.3,
            "maximum": 0.99,
            "description": "Minimum score (0-1) for a pair to count as a match."
          },
          "one_to_many": {
            "type": "boolean",
            "default": false,
            "description": "Return several candidates per source row."
          },
          "max_matches_per_source": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50,
            "description": "Only used with one_to_many."
          }
        },
        "additionalProperties": false
      },
      "JobCreate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Label for the job."
          },
          "source": {
            "type": "array",
            "minItems": 1,
            "description": "Rows to match: a list of flat objects (column -> value).",
            "items": {
              "type": "object",
              "additionalProperties": {
                "nullable": true
              }
            }
          },
          "target": {
            "type": "array",
            "minItems": 1,
            "description": "Rows to match against, same shape as source.",
            "items": {
              "type": "object",
              "additionalProperties": {
                "nullable": true
              }
            }
          },
          "configuration": {
            "$ref": "#/components/schemas/MatchConfiguration"
          }
        },
        "required": [
          "source",
          "target"
        ],
        "additionalProperties": false
      },
      "JobStatistics": {
        "type": "object",
        "properties": {
          "total_source": {
            "type": "integer"
          },
          "total_target": {
            "type": "integer"
          },
          "matched_count": {
            "type": "integer"
          },
          "matched_source_count": {
            "type": "integer"
          },
          "unmatched_source_count": {
            "type": "integer"
          },
          "avg_score": {
            "type": "number"
          },
          "threshold": {
            "type": "number"
          },
          "one_to_many": {
            "type": "boolean"
          },
          "duration_ms": {
            "type": "integer"
          }
        },
        "additionalProperties": false
      },
      "Job": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "default": "job"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "pending",
              "processing",
              "retrying",
              "completed",
              "failed"
            ]
          },
          "source_rows": {
            "type": "integer"
          },
          "target_rows": {
            "type": "integer"
          },
          "progress": {
            "type": "integer",
            "nullable": true
          },
          "statistics": {
            "anyOf": [
              {
                "type": "object",
                "nullable": true
              },
              {
                "$ref": "#/components/schemas/JobStatistics"
              }
            ]
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "results_url": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "object",
          "results_url",
          "source_rows",
          "status",
          "target_rows"
        ],
        "additionalProperties": false
      },
      "Page": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "next_offset": {
            "type": "integer",
            "nullable": true
          }
        },
        "required": [
          "limit",
          "next_offset",
          "offset",
          "total"
        ],
        "additionalProperties": false
      },
      "JobList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Job"
            }
          },
          "page": {
            "$ref": "#/components/schemas/Page"
          }
        },
        "required": [
          "data",
          "page"
        ],
        "additionalProperties": false
      },
      "Match": {
        "type": "object",
        "properties": {
          "source_index": {
            "type": "integer",
            "description": "Position in the submitted source list",
            "nullable": true
          },
          "target_index": {
            "type": "integer",
            "description": "Position in the submitted target list",
            "nullable": true
          },
          "score": {
            "type": "number"
          },
          "confidence": {
            "type": "string",
            "nullable": true
          },
          "confidence_label": {
            "type": "string",
            "nullable": true
          },
          "review_status": {
            "type": "string",
            "nullable": true
          },
          "field_scores": {
            "type": "object",
            "additionalProperties": {}
          },
          "matched_fields": {
            "type": "array",
            "items": {}
          },
          "explanation": {
            "type": "string",
            "nullable": true
          },
          "source": {
            "type": "object",
            "additionalProperties": {
              "nullable": true
            }
          },
          "target": {
            "type": "object",
            "additionalProperties": {
              "nullable": true
            }
          }
        },
        "required": [
          "score"
        ],
        "additionalProperties": false
      },
      "JobResults": {
        "type": "object",
        "properties": {
          "job_id": {
            "type": "string"
          },
          "statistics": {
            "anyOf": [
              {
                "type": "object",
                "nullable": true
              },
              {
                "$ref": "#/components/schemas/JobStatistics"
              }
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Match"
            }
          },
          "page": {
            "$ref": "#/components/schemas/Page"
          }
        },
        "required": [
          "data",
          "job_id",
          "page"
        ],
        "additionalProperties": false
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "default": "webhook_endpoint"
          },
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "active": {
            "type": "boolean"
          },
          "failure_count": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_delivery_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "required": [
          "active",
          "events",
          "failure_count",
          "id",
          "object",
          "url"
        ],
        "additionalProperties": false
      },
      "WebhookList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Webhook"
            }
          }
        },
        "required": [
          "data"
        ],
        "additionalProperties": false
      },
      "WebhookCreate": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500,
            "description": "HTTPS URL on a public host."
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "job.completed",
                "job.failed",
                "*"
              ]
            }
          }
        },
        "required": [
          "url"
        ],
        "additionalProperties": false
      },
      "WebhookWithSecret": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "default": "webhook_endpoint"
          },
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "active": {
            "type": "boolean"
          },
          "failure_count": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_delivery_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "secret": {
            "type": "string",
            "description": "Signing secret. Returned only when the endpoint is created (not on an idempotent replay); store it now."
          }
        },
        "required": [
          "active",
          "events",
          "failure_count",
          "id",
          "object",
          "url"
        ],
        "additionalProperties": false
      },
      "WebhookUpdate": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "job.completed",
                "job.failed",
                "*"
              ]
            }
          },
          "active": {
            "type": "boolean",
            "description": "Set true to re-enable an endpoint disabled after failures."
          }
        },
        "additionalProperties": false
      },
      "WebhookTest": {
        "type": "object",
        "properties": {
          "event_id": {
            "type": "string"
          },
          "status": {
            "type": "string"
          }
        },
        "required": [
          "event_id",
          "status"
        ],
        "additionalProperties": false
      },
      "PlanLimits": {
        "type": "object",
        "properties": {
          "max_rows_per_file": {
            "type": "integer",
            "nullable": true
          },
          "jobs_per_day": {
            "type": "integer",
            "nullable": true
          },
          "rows_per_month": {
            "type": "integer",
            "nullable": true
          },
          "api_calls_per_day": {
            "type": "integer"
          }
        },
        "required": [
          "api_calls_per_day"
        ],
        "additionalProperties": false
      },
      "Account": {
        "type": "object",
        "properties": {
          "user_id": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "plan": {
            "type": "string"
          },
          "plan_name": {
            "type": "string"
          },
          "limits": {
            "$ref": "#/components/schemas/PlanLimits"
          },
          "api_key": {
            "type": "object",
            "description": "The key used for this request (never the secret).",
            "additionalProperties": {
              "nullable": true
            }
          },
          "rate_limit": {
            "type": "object",
            "description": "limit, remaining and reset (unix time) for today.",
            "additionalProperties": {
              "type": "integer"
            }
          }
        },
        "required": [
          "api_key",
          "email",
          "limits",
          "plan",
          "plan_name",
          "rate_limit",
          "user_id"
        ],
        "additionalProperties": false
      }
    },
    "responses": {
      "UNPROCESSABLE_ENTITY": {
        "description": "Unprocessable Entity",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "DEFAULT_ERROR": {
        "description": "Default error response",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Authorization: Bearer fm_live_..."
      },
      "ApiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key"
      }
    }
  }
}
