API reference

GEDCOM

Import a GEDCOM file into a workspace, and export a workspace as GEDCOM. Both run in the background: start, then poll the job.

Paths are relative to https://kleiobase.com/api/v1.

post/workspaces/{workspace_id}/gedcom/imports

Upload a GEDCOM file to import

Starts reading the file in the background; nothing is added to the workspace yet. Poll GET .../gedcom/imports/{import_id} until status is awaiting_confirmation, check the analysis and the plan limit check, then confirm. Counts against the upload rate limit as well. Not idempotent: a retry uploads the file again.

NameInRequiredDescription
workspace_idpathyesThe workspace (project) id.
Request body (multipart/form-data, required)
{
  "type": "object",
  "required": [
    "file"
  ],
  "properties": {
    "file": {
      "type": "string",
      "format": "binary",
      "description": "A .ged file (up to 75 MB), or a .gdz (GEDZIP) archive up to 300 MB. A GEDZIP archive named .zip is accepted too."
    }
  }
}

Responses

  • 202 The import job, analyzing. (GedcomImportStarted)
  • 401 Missing, malformed, unknown or revoked API key. (ErrorResponse)
  • 403 The account has no paid plan, or the key is read-only (a read_write key is required). (ErrorResponse)
  • 404 The workspace does not exist or is not visible to this key's owner. (ErrorResponse)
  • 409 The workspace is archived (writes are refused). (ErrorResponse)
  • 422 Not multipart, no file, not a .ged, .gdz or .zip file, or the file is too large. (ErrorResponse)
  • 429 Rate limit exceeded. Wait the number of seconds in Retry-After. (ErrorResponse)
  • 500 Unexpected error. Retry later and quote request_id if it persists. (ErrorResponse)
202 response schema
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
      "description": "Poll GET .../gedcom/imports/{import_id}."
    },
    "status": {
      "type": "string",
      "enum": [
        "analyzing",
        "awaiting_confirmation",
        "importing",
        "completed",
        "failed",
        "cancelled",
        "expired"
      ]
    }
  },
  "required": [
    "id",
    "status"
  ]
}
get/workspaces/{workspace_id}/gedcom/imports/{import_id}

Get a GEDCOM import

Poll this after uploading. When awaiting_confirmation, `analysis` describes the file and the cap_check fields say whether each import mode fits the plan's person limit. When completed, `result` says what was added.

NameInRequiredDescription
workspace_idpathyesThe workspace (project) id.
import_idpathyesThe GEDCOM import id, from POST .../gedcom/imports.

Responses

  • 200 The import job. (GedcomImport)
  • 401 Missing, malformed, unknown or revoked API key. (ErrorResponse)
  • 403 The account has no paid plan. (ErrorResponse)
  • 404 The workspace does not exist or is not visible to this key's owner. Also: the import is not in this workspace. (ErrorResponse)
  • 429 Rate limit exceeded. Wait the number of seconds in Retry-After. (ErrorResponse)
  • 500 Unexpected error. Retry later and quote request_id if it persists. (ErrorResponse)
200 response schema
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "status": {
      "type": "string",
      "enum": [
        "analyzing",
        "awaiting_confirmation",
        "importing",
        "completed",
        "failed",
        "cancelled",
        "expired"
      ],
      "description": "analyzing -> awaiting_confirmation -> (confirm) importing -> completed. failed when the file could not be read or the import broke; cancelled when cancelled in the app; expired when it was never confirmed (the uploaded file is then deleted)."
    },
    "file_name": {
      "type": [
        "string",
        "null"
      ]
    },
    "progress": {
      "anyOf": [
        {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        {
          "type": "null"
        }
      ],
      "description": "0-100 while analyzing."
    },
    "analysis": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "detected_version": {
              "description": "The GEDCOM version the file declares, for example 5.5.1 or 7.0.",
              "type": [
                "string",
                "null"
              ]
            },
            "people": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991
            },
            "families": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991
            },
            "sources": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991,
              "description": "Source (SOUR) records in the file. They are not imported: an import adds people and families only."
            },
            "events": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991
            },
            "media": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991,
              "description": "Images bundled in a .gdz archive. 0 for a .ged file."
            },
            "existing_people": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991,
              "description": "People in the file who are already in this workspace (from an earlier import)."
            },
            "changed_people": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991,
              "description": "Of existing_people, how many changed in the file since they were last imported."
            },
            "warnings": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991,
              "description": "Problems found while reading the file. The import can still go ahead."
            },
            "notes": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991,
              "description": "Routine conversion notes."
            }
          },
          "required": [
            "detected_version",
            "people",
            "families",
            "sources",
            "events",
            "media",
            "existing_people",
            "changed_people",
            "warnings",
            "notes"
          ]
        },
        {
          "type": "null"
        }
      ],
      "description": "What the file holds. Set from awaiting_confirmation on; null while analyzing, after a failure, and once completed."
    },
    "cap_check": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "allowed": {
              "type": "boolean",
              "description": "false: importing this many people would pass the plan's person limit, and confirm is refused."
            },
            "needed": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991,
              "description": "People this import would add."
            },
            "used": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991,
              "description": "People the account already has."
            },
            "limit": {
              "anyOf": [
                {
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991
                },
                {
                  "type": "null"
                }
              ],
              "description": "The plan's person limit. Null when unlimited."
            },
            "remaining": {
              "anyOf": [
                {
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991
                },
                {
                  "type": "null"
                }
              ],
              "description": "People that can still be added. Null when unlimited."
            }
          },
          "required": [
            "allowed",
            "needed",
            "used",
            "limit",
            "remaining"
          ]
        },
        {
          "type": "null"
        }
      ],
      "description": "The plan limit check for mode all. Null until analyzed."
    },
    "cap_check_skip": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "allowed": {
              "type": "boolean",
              "description": "false: importing this many people would pass the plan's person limit, and confirm is refused."
            },
            "needed": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991,
              "description": "People this import would add."
            },
            "used": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991,
              "description": "People the account already has."
            },
            "limit": {
              "anyOf": [
                {
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991
                },
                {
                  "type": "null"
                }
              ],
              "description": "The plan's person limit. Null when unlimited."
            },
            "remaining": {
              "anyOf": [
                {
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991
                },
                {
                  "type": "null"
                }
              ],
              "description": "People that can still be added. Null when unlimited."
            }
          },
          "required": [
            "allowed",
            "needed",
            "used",
            "limit",
            "remaining"
          ]
        },
        {
          "type": "null"
        }
      ],
      "description": "The plan limit check for mode skip."
    },
    "cap_check_changed": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "allowed": {
              "type": "boolean",
              "description": "false: importing this many people would pass the plan's person limit, and confirm is refused."
            },
            "needed": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991,
              "description": "People this import would add."
            },
            "used": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991,
              "description": "People the account already has."
            },
            "limit": {
              "anyOf": [
                {
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991
                },
                {
                  "type": "null"
                }
              ],
              "description": "The plan's person limit. Null when unlimited."
            },
            "remaining": {
              "anyOf": [
                {
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991
                },
                {
                  "type": "null"
                }
              ],
              "description": "People that can still be added. Null when unlimited."
            }
          },
          "required": [
            "allowed",
            "needed",
            "used",
            "limit",
            "remaining"
          ]
        },
        {
          "type": "null"
        }
      ],
      "description": "The plan limit check for mode changed."
    },
    "can_import_changes": {
      "type": "boolean",
      "description": "Whether the plan allows mode changed (Researcher and above)."
    },
    "result": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "people": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991,
              "description": "People added."
            },
            "families": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991,
              "description": "Families added."
            },
            "sources": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991,
              "description": "Always 0: source (SOUR) records are not imported."
            },
            "media": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991
            },
            "skipped_people": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991,
              "description": "People not added because they were already in the workspace (mode skip or changed)."
            }
          },
          "required": [
            "people",
            "families",
            "sources",
            "media",
            "skipped_people"
          ]
        },
        {
          "type": "null"
        }
      ],
      "description": "What was imported. Set only when completed."
    },
    "error": {
      "description": "Why it failed, when status is failed.",
      "type": [
        "string",
        "null"
      ]
    },
    "created_at": {
      "type": "string"
    },
    "completed_at": {
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "id",
    "status",
    "file_name",
    "progress",
    "analysis",
    "cap_check",
    "cap_check_skip",
    "cap_check_changed",
    "can_import_changes",
    "result",
    "error",
    "created_at",
    "completed_at"
  ]
}
delete/workspaces/{workspace_id}/gedcom/imports/{import_id}

Cancel a GEDCOM import

Cancels an import that is awaiting confirmation, as cancelling the preview in the app does: nothing is added to the workspace and the uploaded file is deleted. The status becomes cancelled. Replays of an Idempotency-Key return the original body and request_id; headers are not replayed.

NameInRequiredDescription
workspace_idpathyesThe workspace (project) id.
import_idpathyesThe GEDCOM import id, from POST .../gedcom/imports.
Idempotency-KeyheadernoMakes the write safe to retry (1-255 characters, kept 24 hours). Same key and same request replays the original response; a different request with the same key is a 409.

Responses

  • 200 Cancelled. (Ok)
  • 401 Missing, malformed, unknown or revoked API key. (ErrorResponse)
  • 403 The account has no paid plan, or the key is read-only (a read_write key is required). (ErrorResponse)
  • 404 The workspace does not exist or is not visible to this key's owner. Also: the import is not in this workspace. (ErrorResponse)
  • 409 The workspace is archived (writes are refused). Also: Idempotency-Key was reused with a different request, or a request with it is still in progress. Also: the import is not awaiting confirmation (still analyzing, already confirmed, failed, cancelled or expired). (ErrorResponse)
  • 422 Validation failed. A bad Idempotency-Key (1-255 characters) is also a 422. (ErrorResponse)
  • 429 Rate limit exceeded. Wait the number of seconds in Retry-After. (ErrorResponse)
  • 500 Unexpected error. Retry later and quote request_id if it persists. (ErrorResponse)
200 response schema
{
  "type": "object",
  "properties": {
    "ok": {
      "type": "boolean",
      "const": true
    }
  },
  "required": [
    "ok"
  ]
}
post/workspaces/{workspace_id}/gedcom/imports/{import_id}/confirm

Confirm a GEDCOM import

Adds the people, families and sources to the workspace in the background, as confirming the import in the app does. Poll GET .../gedcom/imports/{import_id} until completed. Counts against the upload rate limit as well. Replays of an Idempotency-Key return the original body and request_id; headers are not replayed.

NameInRequiredDescription
workspace_idpathyesThe workspace (project) id.
import_idpathyesThe GEDCOM import id, from POST .../gedcom/imports.
Idempotency-KeyheadernoMakes the write safe to retry (1-255 characters, kept 24 hours). Same key and same request replays the original response; a different request with the same key is a 409.
Request body (application/json)
{
  "type": "object",
  "properties": {
    "mode": {
      "description": "all (default): import everyone in the file. skip: leave out people already in the workspace. changed: like skip, but also import the existing people who changed in the file (Researcher plan and above).",
      "type": "string",
      "enum": [
        "all",
        "skip",
        "changed"
      ]
    }
  },
  "additionalProperties": false
}

Responses

  • 202 Importing. (GedcomImportStarted)
  • 401 Missing, malformed, unknown or revoked API key. (ErrorResponse)
  • 403 The account has no paid plan, or the key is read-only (a read_write key is required). Also: the import would pass the plan's person limit (details: limit, used, needed, plan), or mode changed needs the Researcher plan or above. (ErrorResponse)
  • 404 The workspace does not exist or is not visible to this key's owner. Also: the import is not in this workspace. (ErrorResponse)
  • 409 The workspace is archived (writes are refused). Also: Idempotency-Key was reused with a different request, or a request with it is still in progress. Also: the import is not awaiting confirmation (still analyzing, already confirmed, failed, cancelled or expired), or its file is no longer available. (ErrorResponse)
  • 422 Validation failed. A bad Idempotency-Key (1-255 characters) is also a 422. Also: Invalid body. (ErrorResponse)
  • 429 Rate limit exceeded. Wait the number of seconds in Retry-After. (ErrorResponse)
  • 500 Unexpected error. Retry later and quote request_id if it persists. (ErrorResponse)
202 response schema
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
      "description": "Poll GET .../gedcom/imports/{import_id}."
    },
    "status": {
      "type": "string",
      "enum": [
        "analyzing",
        "awaiting_confirmation",
        "importing",
        "completed",
        "failed",
        "cancelled",
        "expired"
      ]
    }
  },
  "required": [
    "id",
    "status"
  ]
}
post/workspaces/{workspace_id}/gedcom/exports

Export the workspace as GEDCOM

Builds the file in the background. Poll GET .../gedcom/exports/{export_id} until ready for a download URL. No email is sent. One export at a time per account. Archived workspaces can be exported. Replays of an Idempotency-Key return the original body and request_id; headers are not replayed.

NameInRequiredDescription
workspace_idpathyesThe workspace (project) id.
Idempotency-KeyheadernoMakes the write safe to retry (1-255 characters, kept 24 hours). Same key and same request replays the original response; a different request with the same key is a 409.
Request body (application/json)
{
  "type": "object",
  "properties": {
    "version": {
      "description": "GEDCOM version to write. Default 7.0.18.",
      "type": "string",
      "enum": [
        "7.0.18",
        "5.5.1"
      ]
    },
    "format": {
      "description": "ged (default): the tree as text. gdz: a GEDZIP archive with the record images.",
      "type": "string",
      "enum": [
        "ged",
        "gdz"
      ]
    },
    "filename": {
      "description": "Base name for the downloaded file, without extension. Unsafe characters are removed. Default kleiobase-export.",
      "type": "string",
      "maxLength": 255
    },
    "redact_living": {
      "description": "Leave out the details of living people (default false). GET .../gedcom/exports/redaction-preview lists who that is.",
      "type": "boolean"
    }
  },
  "additionalProperties": false
}

Responses

  • 202 The export job, queued. (GedcomExportStarted)
  • 401 Missing, malformed, unknown or revoked API key. (ErrorResponse)
  • 403 The account has no paid plan, or the key is read-only (a read_write key is required). (ErrorResponse)
  • 404 The workspace does not exist or is not visible to this key's owner. (ErrorResponse)
  • 409 Idempotency-Key was reused with a different request, or a request with it is still in progress. Also: an export is already being prepared. (ErrorResponse)
  • 422 Validation failed. A bad Idempotency-Key (1-255 characters) is also a 422. Also: Invalid body. (ErrorResponse)
  • 429 Rate limit exceeded. Wait the number of seconds in Retry-After. (ErrorResponse)
  • 500 Unexpected error. Retry later and quote request_id if it persists. (ErrorResponse)
202 response schema
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
      "description": "Poll GET .../gedcom/exports/{export_id}."
    },
    "status": {
      "type": "string",
      "enum": [
        "queued",
        "generating",
        "ready",
        "failed"
      ]
    }
  },
  "required": [
    "id",
    "status"
  ]
}
get/workspaces/{workspace_id}/gedcom/exports/{export_id}

Get a GEDCOM export

When ready, `download` has a signed URL that works for 5 minutes; poll again for a new one. Finished exports are deleted after 7 days.

NameInRequiredDescription
workspace_idpathyesThe workspace (project) id.
export_idpathyesThe GEDCOM export id, from POST .../gedcom/exports.

Responses

  • 200 The export job. (GedcomExport)
  • 401 Missing, malformed, unknown or revoked API key. (ErrorResponse)
  • 403 The account has no paid plan. (ErrorResponse)
  • 404 The workspace does not exist or is not visible to this key's owner. Also: the export is not in this workspace. (ErrorResponse)
  • 429 Rate limit exceeded. Wait the number of seconds in Retry-After. (ErrorResponse)
  • 500 Unexpected error. Retry later and quote request_id if it persists. (ErrorResponse)
200 response schema
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "status": {
      "type": "string",
      "enum": [
        "queued",
        "generating",
        "ready",
        "failed"
      ],
      "description": "queued -> generating -> ready, or failed."
    },
    "version": {
      "type": "string"
    },
    "format": {
      "type": "string",
      "enum": [
        "ged",
        "gdz"
      ]
    },
    "filename": {
      "type": "string",
      "description": "The download's base name; the file is <filename>.<format>."
    },
    "redact_living": {
      "type": "boolean"
    },
    "file_size_bytes": {
      "anyOf": [
        {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        {
          "type": "null"
        }
      ],
      "description": "Set when ready."
    },
    "warnings": {
      "anyOf": [
        {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        {
          "type": "null"
        }
      ],
      "description": "Details the export had to change or leave out, for example for GEDCOM 5.5.1. Set when ready."
    },
    "error": {
      "description": "Why it failed, when status is failed.",
      "type": [
        "string",
        "null"
      ]
    },
    "download": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "url": {
              "type": "string",
              "description": "Signed download URL. It works for 5 minutes; poll again for a new one."
            },
            "expires_at": {
              "type": "string"
            }
          },
          "required": [
            "url",
            "expires_at"
          ]
        },
        {
          "type": "null"
        }
      ],
      "description": "Set only when ready."
    },
    "created_at": {
      "type": "string"
    },
    "completed_at": {
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "id",
    "status",
    "version",
    "format",
    "filename",
    "redact_living",
    "file_size_bytes",
    "warnings",
    "error",
    "download",
    "created_at",
    "completed_at"
  ]
}
get/workspaces/{workspace_id}/gedcom/exports/redaction-preview

Preview who redact_living hides

The people whose details an export with redact_living leaves out, by the same rule the export uses.

NameInRequiredDescription
workspace_idpathyesThe workspace (project) id.

Responses

  • 200 The people that would be hidden. (GedcomRedactionPreview)
  • 401 Missing, malformed, unknown or revoked API key. (ErrorResponse)
  • 403 The account has no paid plan. (ErrorResponse)
  • 404 The workspace does not exist or is not visible to this key's owner. (ErrorResponse)
  • 429 Rate limit exceeded. Wait the number of seconds in Retry-After. (ErrorResponse)
  • 500 Unexpected error. Retry later and quote request_id if it persists. (ErrorResponse)
200 response schema
{
  "type": "object",
  "properties": {
    "living_people": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "person_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "person_id",
          "name"
        ]
      },
      "description": "People whose details an export with redact_living leaves out."
    },
    "total_people": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991,
      "description": "People in the workspace (the export includes all of them)."
    }
  },
  "required": [
    "living_people",
    "total_people"
  ]
}

We use cookies and similar technologies. Essential cookies keep the site working and secure. We only load analytics (PostHog, including masked session replays) and marketing (Meta Pixel and TikTok Pixel) with your consent. See our Privacy Policy.