{"ok": true, "next": null, "next_url": null, "rows": [{"id": "json_api:databaseforeignkeytargetsview", "page": "json_api", "ref": "databaseforeignkeytargetsview", "title": "Database foreign key targets", "content": "The  /<database>/-/foreign-key-targets  endpoint returns the list of tables in a database that can be referenced by a single-column foreign key. This requires the  create-table  permission. \n                 GET /<database>/-/foreign-key-targets \n                 The response includes only tables with exactly one primary key column. Hidden tables, tables with compound primary keys and tables with no explicit primary key are omitted. \n                 Each target includes the normalized SQLite type affinity for the primary key column in  type . The type is calculated using SQLite's documented affinity rules:  INT  maps to  integer ;  CHAR ,  CLOB  or  TEXT  maps to  text ;  BLOB  or no type maps to  blob ;  REAL  and floating-point declared types map to  real ; everything else maps to  numeric . \n                 {\n    \"ok\": true,\n    \"database\": \"data\",\n    \"targets\": [\n        {\n            \"fk_table\": \"owners\",\n            \"fk_column\": \"id\",\n            \"type\": \"integer\"\n        },\n        {\n            \"fk_table\": \"categories\",\n            \"fk_column\": \"slug\",\n            \"type\": \"text\"\n        }\n    ]\n}", "breadcrumbs": "[\"JSON API\", \"The JSON write API\"]", "references": "[]"}, {"id": "json_api:executewriteview", "page": "json_api", "ref": "executewriteview", "title": "Executing write SQL", "content": "Actors with the  execute-write-sql  permission can execute arbitrary writable SQL against a mutable database using  /-/execute-write . \n                 POST /<database>/-/execute-write\nContent-Type: application/json\nAuthorization: Bearer dstok_<rest-of-token> \n                 The request body must include a  \"sql\"  string. Named SQL parameters can be provided using the optional  \"params\"  object: \n                 {\n    \"sql\": \"insert into dogs (name) values (:name)\",\n    \"params\": {\n        \"name\": \"Cleo\"\n    }\n} \n                 The SQL must be writable. Read-only  select  queries should use the regular  custom SQL query JSON API  instead. \n                 Datasette analyzes the SQL before executing it. The actor must have  execute-write-sql  permission for the database, and must also have any permissions required by the operations in the SQL. For example, inserts and updates against a table require  insert-row ,  update-row  and  delete-row  permissions for that table. Reads performed as part of the write, such as  insert into dogs select ... from other_table , require  view-table  permission on the source table. Schema changes require  create-table ,  alter-table  or  drop-table  permissions as appropriate. \n                 Unsupported SQL operations are rejected by default.  VACUUM  is not allowed in arbitrary write SQL, and writes to SQLite virtual tables or shadow tables are rejected. SQL functions are allowed and are not separately restricted by Datasette permissions. \n                 A successful response includes a message, the SQLite  rowcount , a  \"rows\" \n                    list, a  \"truncated\"  flag and a summary of the operations that were executed: \n                 The shape of the  \"analysis\"  block is not part of the  stable API  and may change in future Datasette releases. \n                 {\n    \"ok\": true,\n    \"message\": \"Query executed, 1 row affected\",\n    \"rowcount\": 1,\n    \"rows\": [],\n    \"truncated\": false,\n    \"analysis\": [\n        {\n            \"operation\": \"insert\",\n            \"database\": \"data\",\n            \"table\": \"dogs\",\n            \"required_permission\": \"insert-row, update-row, delete-row\",\n            \"source\": null\n        }\n    ]\n} \n                 If SQLite reports  -1  for the row count, the message will be  \"Query executed\" . \n                 For most write statements  \"rows\"  will be an empty list and  \"truncated\" \n                    will be  false . If the SQL uses SQLite's  RETURNING  clause,  \"rows\" \n                    will contain returned rows using the same default representation as table and\n                    query JSON responses.  \"truncated\"  indicates if more rows were returned than\n                    the execute-write returning row limit, which defaults to 10: \n                 {\n    \"ok\": true,\n    \"message\": \"Query executed, 1 row affected\",\n    \"rowcount\": 1,\n    \"rows\": [\n        {\n            \"id\": 1,\n            \"name\": \"Cleo\"\n        }\n    ],\n    \"truncated\": false,\n    \"analysis\": [\n        {\n            \"operation\": \"insert\",\n            \"database\": \"data\",\n            \"table\": \"dogs\",\n            \"required_permission\": \"insert-row, update-row, delete-row\",\n            \"source\": null\n        },\n        {\n            \"operation\": \"read\",\n            \"database\": \"data\",\n            \"table\": \"dogs\",\n            \"required_permission\": \"view-table\",\n            \"source\": null\n        }\n    ]\n} \n                 Errors use the  standard Datasette error format : \n                 {\n    \"ok\": false,\n    \"error\": \"Permission denied: need execute-write-sql\",\n    \"errors\": [\n        \"Permission denied: need execute-write-sql\"\n    ],\n    \"status\": 403\n}", "breadcrumbs": "[\"JSON API\", \"The JSON write API\"]", "references": "[]"}, {"id": "json_api:rowdeleteview", "page": "json_api", "ref": "rowdeleteview", "title": "Deleting a row", "content": "To delete a row, make a  POST  to  /<database>/<table>/<row-pks>/-/delete . This requires the  delete-row  permission. \n                 POST /<database>/<table>/<row-pks>/-/delete\nContent-Type: application/json\nAuthorization: Bearer dstok_<rest-of-token> \n                 <row-pks>  here is the  tilde-encoded  primary key value of the row to delete - or a comma-separated list of primary key values if the table has a composite primary key. \n                 If successful, this will return a  200  status code and a  {\"ok\": true}  response body. \n                 Any errors will use the  standard error format , with a  400  status code for a bad input or a  403  status code for an authentication or permission error.", "breadcrumbs": "[\"JSON API\", \"The JSON write API\"]", "references": "[]"}, {"id": "json_api:rowupdateview", "page": "json_api", "ref": "rowupdateview", "title": "Updating a row", "content": "To update a row, make a  POST  to  /<database>/<table>/<row-pks>/-/update . This requires the  update-row  permission. \n                 POST /<database>/<table>/<row-pks>/-/update\nContent-Type: application/json\nAuthorization: Bearer dstok_<rest-of-token> \n                 {\n    \"update\": {\n        \"text_column\": \"New text string\",\n        \"integer_column\": 3,\n        \"float_column\": 3.14\n    }\n} \n                 <row-pks>  here is the  tilde-encoded  primary key value of the row to update - or a comma-separated list of primary key values if the table has a composite primary key. \n                 You only need to pass the columns you want to update. Any other columns will be left unchanged. \n                 Updated values can use the  binary value JSON format . \n                 If successful, this will return a  200  status code and a  {\"ok\": true}  response body. \n                 Add  \"return\": true  to the request body to return the updated row: \n                 {\n    \"update\": {\n        \"title\": \"New title\"\n    },\n    \"return\": true\n} \n                 The returned JSON will look like this: \n                 {\n    \"ok\": true,\n    \"rows\": [\n        {\n            \"id\": 1,\n            \"title\": \"New title\",\n            \"other_column\": \"Will be present here too\"\n        }\n    ]\n} \n                 Any errors will use the  standard error format , with a  400  status code for a bad input or a  403  status code for an authentication or permission error. \n                 Pass  \"alter: true  to automatically add any missing columns to the table. This requires the  alter-table  permission.", "breadcrumbs": "[\"JSON API\", \"The JSON write API\"]", "references": "[]"}, {"id": "json_api:tablealterview", "page": "json_api", "ref": "tablealterview", "title": "Altering tables", "content": "To alter an existing table, make a  POST  to  /<database>/<table>/-/alter . This requires the  alter-table  permission. \n                 POST /<database>/<table>/-/alter\nContent-Type: application/json\nAuthorization: Bearer dstok_<rest-of-token> \n                 The request body should include an  operations  array. Each operation has the same top-level shape: an  op  string and an  args  object. \n                 {\n    \"operations\": [\n        {\n            \"op\": \"add_column\",\n            \"args\": {\n                \"name\": \"slug\",\n                \"type\": \"text\",\n                \"not_null\": true,\n                \"default\": \"\"\n            }\n        },\n        {\n            \"op\": \"add_column\",\n            \"args\": {\n                \"name\": \"created\",\n                \"type\": \"text\",\n                \"default_expr\": \"current_timestamp\"\n            }\n        },\n        {\n            \"op\": \"rename_column\",\n            \"args\": {\n                \"name\": \"title\",\n                \"to\": \"headline\"\n            }\n        },\n        {\n            \"op\": \"rename_table\",\n            \"args\": {\n                \"to\": \"published_posts\"\n            }\n        },\n        {\n            \"op\": \"alter_column\",\n            \"args\": {\n                \"name\": \"score\",\n                \"type\": \"float\"\n            }\n        },\n        {\n            \"op\": \"drop_column\",\n            \"args\": {\n                \"name\": \"draft_notes\"\n            }\n        },\n        {\n            \"op\": \"set_primary_key\",\n            \"args\": {\n                \"columns\": [\"id\"]\n            }\n        },\n        {\n            \"op\": \"add_foreign_key\",\n            \"args\": {\n                \"column\": \"owner_id\",\n                \"fk_table\": \"owners\"\n            }\n        },\n        {\n            \"op\": \"drop_foreign_key\",\n            \"args\": {\n                \"column\": \"old_owner_id\"\n            }\n        },\n        {\n            \"op\": \"set_foreign_keys\",\n            \"args\": {\n                \"foreign_keys\": [\n                    {\n                        \"column\": \"owner_id\",\n                        \"fk_table\": \"owners\",\n                        \"fk_column\": \"id\"\n                    }\n                ]\n            }\n        },\n        {\n            \"op\": \"reorder_columns\",\n            \"args\": {\n                \"columns\": [\"id\", \"headline\", \"slug\", \"created\", \"score\"]\n            }\n        }\n    ]\n} \n                 Supported operations: \n                 \n                     \n                         add_column  adds a new column.  args  accepts  name , optional  type  of  text ,  integer ,  float  or  blob , optional  not_null , optional literal  default  and optional  default_expr . If  not_null  is  true  either a non-null  default  or  default_expr  is required. \n                     \n                     \n                         rename_column  renames a column.  args  accepts  name  and  to . \n                     \n                     \n                         rename_table  renames the table.  args  accepts  to , the new table name. If combined with other operations, Datasette applies the column, primary key, foreign key and column order changes before renaming the table. \n                     \n                     \n                         alter_column  changes column properties.  args  accepts  name  and at least one of  type ,  not_null , literal  default  or  default_expr . Passing  \"default\": null  removes an existing default. \n                     \n                     \n                         drop_column  drops a column.  args  accepts  name . \n                     \n                     \n                         set_primary_key  changes the table primary key.  args  accepts  columns , a list of one or more column names. \n                     \n                     \n                         add_foreign_key  adds a single-column foreign key constraint.  args  accepts  column ,  fk_table  and optional  fk_column . If  fk_column  is omitted, Datasette will use the single primary key of  fk_table . \n                     \n                     \n                         drop_foreign_key  removes the foreign key constraint for a column.  args  accepts  column . \n                     \n                     \n                         set_foreign_keys  replaces all foreign key constraints on the table.  args  accepts  foreign_keys , a list of objects that each have  column ,  fk_table  and optional  fk_column . An empty list removes all foreign key constraints. \n                     \n                     \n                         reorder_columns  reorders columns.  args  accepts  columns , a list of one or more column names. Columns omitted from this list will appear afterwards in their existing order. \n                     \n                 \n                 default  is always treated as a literal value.  default_expr  accepts the values shown in  default_expr values  and is rendered as the corresponding SQLite default expression. \n                 For foreign key operations that omit  fk_column , the referenced  fk_table  must have a single-column primary key. Datasette will return an error if it cannot identify a single primary key column for that table. \n                 A successful response returns the new schema and the previous schema. If the request used  rename_table ,  table ,  table_url  and  table_api_url  will use the new table name. Renaming a table through this endpoint triggers the  RenameTableEvent  event. \n                 {\n    \"ok\": true,\n    \"database\": \"data\",\n    \"table\": \"published_posts\",\n    \"table_url\": \"http://127.0.0.1:8001/data/published_posts\",\n    \"table_api_url\": \"http://127.0.0.1:8001/data/published_posts.json\",\n    \"altered\": true,\n    \"schema\": \"CREATE TABLE ...\",\n    \"before_schema\": \"CREATE TABLE ...\",\n    \"operations_applied\": 11\n} \n                 Any errors will use the  standard error format , with a  400  status code for a bad input or a  403  status code for an authentication or permission error.", "breadcrumbs": "[\"JSON API\", \"The JSON write API\"]", "references": "[]"}, {"id": "json_api:tablecreateview", "page": "json_api", "ref": "tablecreateview", "title": "Creating a table", "content": "To create a table, make a  POST  to  /<database>/-/create . This requires the  create-table  permission. \n                 POST /<database>/-/create\nContent-Type: application/json\nAuthorization: Bearer dstok_<rest-of-token> \n                 {\n    \"table\": \"name_of_new_table\",\n    \"columns\": [\n        {\n            \"name\": \"id\",\n            \"type\": \"integer\"\n        },\n        {\n            \"name\": \"title\",\n            \"type\": \"text\",\n            \"not_null\": true,\n            \"default\": \"Untitled\"\n        },\n        {\n            \"name\": \"created\",\n            \"type\": \"text\",\n            \"default_expr\": \"current_timestamp\"\n        }\n    ],\n    \"pk\": \"id\"\n} \n                 The JSON here describes the table that will be created: \n                 \n                     \n                         table  is the name of the table to create. This field is required. \n                     \n                     \n                         columns  is a list of columns to create. Each column is a dictionary with  name  and  type  keys. \n                         \n                             \n                                 name  is the name of the column. This is required. \n                             \n                             \n                                 type  is the type of the column. This is optional - if not provided,  text  will be assumed. The valid types are  text ,  integer ,  float  and  blob . \n                             \n                             \n                                 not_null  can be set to  true  to create this column with a  NOT NULL  constraint. \n                             \n                             \n                                 default  can be used to set a literal default value for this column. \n                             \n                             \n                                 default_expr  can be used instead of  default  to set a SQLite default expression. See  default_expr values . \n                             \n                             \n                                 fk_table  can be used to create a single-column foreign key constraint referencing another table.  fk_column  is optional and can be used to specify the referenced column - if omitted, Datasette will use the single primary key of  fk_table . \n                             \n                         \n                     \n                     \n                         pk  is the primary key for the table. This is optional - if not provided, Datasette will create a SQLite table with a hidden  rowid  column. \n                         If the primary key is an integer column, it will be configured to automatically increment for each new record. \n                         If you set this to  id  without including an  id  column in the list of  columns , Datasette will create an auto-incrementing integer ID column for you. \n                     \n                     \n                         pks  can be used instead of  pk  to create a compound primary key. It should be a JSON list of column names to use in that primary key. \n                     \n                     \n                         ignore  can be set to  true  to ignore existing rows by primary key if the table already exists. \n                     \n                     \n                         replace  can be set to  true  to replace existing rows by primary key if the table already exists. This requires the  update-row  permission. \n                     \n                     \n                         alter  can be set to  true  if you want to automatically add any missing columns to the table. This requires the  alter-table  permission. \n                     \n                 \n                 \n                 default_expr  accepts these values: \n                 \n                     \n                         \n                         \n                         \n                         \n                             \n                                 \n                                     Value \n                                 \n                                 \n                                     Recommended column type \n                                 \n                                 \n                                     Example inserted value \n                                 \n                             \n                         \n                         \n                             \n                                 \n                                     current_timestamp \n                                 \n                                 \n                                     text \n                                 \n                                 \n                                     2026-05-01 13:34:00 \n                                 \n                             \n                             \n                                 \n                                     current_date \n                                 \n                                 \n                                     text \n                                 \n                                 \n                                     2026-05-01 \n                                 \n                             \n                             \n                                 \n                                     current_time \n                                 \n                                 \n                                     text \n                                 \n                                 \n                                     13:34:00 \n                                 \n                             \n                             \n                                 \n                                     current_unixtime \n                                 \n                                 \n                                     integer \n                                 \n                                 \n                                     1777642440 \n                                 \n                             \n                             \n                                 \n                                     current_unixtime_ms \n                                 \n                                 \n                                     integer \n                                 \n                                 \n                                     1777642440000 \n                                 \n                             \n                         \n                     \n                 \n                 This example creates a foreign key from  projects.owner_id  to the single primary key of  owners : \n                 {\n    \"table\": \"projects\",\n    \"columns\": [\n        {\n            \"name\": \"id\",\n            \"type\": \"integer\"\n        },\n        {\n            \"name\": \"owner_id\",\n            \"type\": \"integer\",\n            \"fk_table\": \"owners\"\n        },\n        {\n            \"name\": \"title\",\n            \"type\": \"text\"\n        }\n    ],\n    \"pk\": \"id\"\n} \n                 If the table is successfully created this will return a  201  status code and the following response: \n                 {\n    \"ok\": true,\n    \"database\": \"data\",\n    \"table\": \"name_of_new_table\",\n    \"table_url\": \"http://127.0.0.1:8001/data/name_of_new_table\",\n    \"table_api_url\": \"http://127.0.0.1:8001/data/name_of_new_table.json\",\n    \"schema\": \"CREATE TABLE [name_of_new_table] (\\n   [id] INTEGER PRIMARY KEY,\\n   [title] TEXT NOT NULL DEFAULT 'Untitled',\\n   [created] TEXT DEFAULT CURRENT_TIMESTAMP\\n)\"\n}", "breadcrumbs": "[\"JSON API\", \"The JSON write API\"]", "references": "[]"}, {"id": "json_api:tablecreateview-example", "page": "json_api", "ref": "tablecreateview-example", "title": "Creating a table from example data", "content": "Instead of specifying  columns  directly you can instead pass a single example  row  or a list of  rows .\n                    Datasette will create a table with a schema that matches those rows and insert them for you: \n                 POST /<database>/-/create\nContent-Type: application/json\nAuthorization: Bearer dstok_<rest-of-token> \n                 {\n    \"table\": \"creatures\",\n    \"rows\": [\n        {\n            \"id\": 1,\n            \"name\": \"Tarantula\"\n        },\n        {\n            \"id\": 2,\n            \"name\": \"K\u0101k\u0101p\u014d\"\n        }\n    ],\n    \"pk\": \"id\"\n} \n                 Example rows can use the  binary value JSON format , allowing Datasette to infer  BLOB  columns. \n                 Doing this requires both the  create-table  and  insert-row  permissions. \n                 The  201  response here will be similar to the  columns  form, but will also include the number of rows that were inserted as  row_count : \n                 {\n    \"ok\": true,\n    \"database\": \"data\",\n    \"table\": \"creatures\",\n    \"table_url\": \"http://127.0.0.1:8001/data/creatures\",\n    \"table_api_url\": \"http://127.0.0.1:8001/data/creatures.json\",\n    \"schema\": \"CREATE TABLE [creatures] (\\n   [id] INTEGER PRIMARY KEY,\\n   [name] TEXT\\n)\",\n    \"row_count\": 2\n} \n                 You can call the create endpoint multiple times for the same table provided you are specifying the table using the  rows  or  row  option. New rows will be inserted into the table each time. This means you can use this API if you are unsure if the relevant table has been created yet. \n                 If you pass a row to the create endpoint with a primary key that already exists you will get an error that looks like this: \n                 {\n    \"ok\": false,\n    \"error\": \"UNIQUE constraint failed: creatures.id\",\n    \"errors\": [\n        \"UNIQUE constraint failed: creatures.id\"\n    ],\n    \"status\": 400\n} \n                 You can avoid this error by passing the same  \"ignore\": true  or  \"replace\": true  options to the create endpoint as you can to the  insert endpoint . \n                 To use the  \"replace\": true  option you will also need the  update-row  permission. \n                 Pass  \"alter\": true  to automatically add any missing columns to the existing table that are present in the rows you are submitting. This requires the  alter-table  permission.", "breadcrumbs": "[\"JSON API\", \"The JSON write API\"]", "references": "[]"}, {"id": "json_api:tabledropview", "page": "json_api", "ref": "tabledropview", "title": "Dropping tables", "content": "To drop a table, make a  POST  to  /<database>/<table>/-/drop . This requires the  drop-table  permission. \n                 POST /<database>/<table>/-/drop\nContent-Type: application/json\nAuthorization: Bearer dstok_<rest-of-token> \n                 Without a POST body this will return a status  200  with a note about how many rows will be deleted: \n                 {\n    \"ok\": true,\n    \"database\": \"<database>\",\n    \"table\": \"<table>\",\n    \"row_count\": 5,\n    \"message\": \"Pass \\\"confirm\\\": true to confirm\"\n} \n                 If you pass the following POST body: \n                 {\n    \"confirm\": true\n} \n                 Then the table will be dropped and a status  200  response of  {\"ok\": true}  will be returned. \n                 Any errors will use the  standard error format , with a  400  status code for a bad input or a  403  status code for an authentication or permission error.", "breadcrumbs": "[\"JSON API\", \"The JSON write API\"]", "references": "[]"}, {"id": "json_api:tableforeignkeysuggestionsview", "page": "json_api", "ref": "tableforeignkeysuggestionsview", "title": "Table foreign key suggestions", "content": "The  /<database>/<table>/-/foreign-key-suggestions  endpoint suggests possible single-column foreign key relationships for a table. This requires the  alter-table  permission. \n                 GET /<database>/<table>/-/foreign-key-suggestions \n                 The response includes every type-compatible single-column primary key target for each column in  options . Datasette also performs a bounded data check against up to 500 rows in the table: if the sampled non-null values for a column all exist in a target primary key, that target is included in  suggestions . \n                 If the bounded check takes too long, the endpoint fails open. It still returns the type-compatible  options  for each column, but  row_check.status  will be  \"timed_out\"  and there may be no  suggestions . \n                 {\n    \"ok\": true,\n    \"database\": \"data\",\n    \"table\": \"projects\",\n    \"row_check\": {\n        \"attempted\": true,\n        \"status\": \"completed\",\n        \"row_limit\": 500,\n        \"sampled_rows\": 3,\n        \"checked_options\": 4\n    },\n    \"columns\": [\n        {\n            \"column\": \"owner_id\",\n            \"type\": \"INTEGER\",\n            \"affinity\": \"integer\",\n            \"current\": null,\n            \"suggestions\": [\n                {\n                    \"fk_table\": \"owners\",\n                    \"fk_column\": \"id\",\n                    \"confidence\": \"sampled\",\n                    \"sampled_values\": 3,\n                    \"reasons\": [\n                        \"type_match\",\n                        \"sample_values_exist\",\n                        \"name_match\"\n                    ]\n                }\n            ],\n            \"options\": [\n                {\n                    \"fk_table\": \"owners\",\n                    \"fk_column\": \"id\",\n                    \"type\": \"INTEGER\"\n                }\n            ]\n        }\n    ]\n}", "breadcrumbs": "[\"JSON API\", \"The JSON write API\"]", "references": "[]"}, {"id": "json_api:tableinsertview", "page": "json_api", "ref": "tableinsertview", "title": "Inserting rows", "content": "This requires the  insert-row  permission. \n                 A single row can be inserted using the  \"row\"  key: \n                 POST /<database>/<table>/-/insert\nContent-Type: application/json\nAuthorization: Bearer dstok_<rest-of-token> \n                 {\n    \"row\": {\n        \"column1\": \"value1\",\n        \"column2\": \"value2\"\n    }\n} \n                 Column values can use the  binary value JSON format  to write BLOB data. \n                 If successful, this will return a  201  status code and the newly inserted row, for example: \n                 {\n    \"rows\": [\n        {\n            \"id\": 1,\n            \"column1\": \"value1\",\n            \"column2\": \"value2\"\n        }\n    ]\n} \n                 To insert multiple rows at a time, use the same API method but send a list of dictionaries as the  \"rows\"  key: \n                 POST /<database>/<table>/-/insert\nContent-Type: application/json\nAuthorization: Bearer dstok_<rest-of-token> \n                 {\n    \"rows\": [\n        {\n            \"column1\": \"value1\",\n            \"column2\": \"value2\"\n        },\n        {\n            \"column1\": \"value3\",\n            \"column2\": \"value4\"\n        }\n    ]\n} \n                 If successful, this will return a  201  status code and a  {\"ok\": true}  response body. \n                 The maximum number rows that can be submitted at once defaults to 100, but this can be changed using the  max_insert_rows  setting. \n                 To return the newly inserted rows, add the  \"return\": true  key to the request body: \n                 {\n    \"rows\": [\n        {\n            \"column1\": \"value1\",\n            \"column2\": \"value2\"\n        },\n        {\n            \"column1\": \"value3\",\n            \"column2\": \"value4\"\n        }\n    ],\n    \"return\": true\n} \n                 This will return the same  \"rows\"  key as the single row example above. There is a small performance penalty for using this option. \n                 If any of your rows have a primary key that is already in use, you will get an error and none of the rows will be inserted: \n                 {\n    \"ok\": false,\n    \"error\": \"UNIQUE constraint failed: new_table.id\",\n    \"errors\": [\n        \"UNIQUE constraint failed: new_table.id\"\n    ],\n    \"status\": 400\n} \n                 Pass  \"ignore\": true  to ignore these errors and insert the other rows: \n                 {\n    \"rows\": [\n        {\n            \"id\": 1,\n            \"column1\": \"value1\",\n            \"column2\": \"value2\"\n        },\n        {\n            \"id\": 2,\n            \"column1\": \"value3\",\n            \"column2\": \"value4\"\n        }\n    ],\n    \"ignore\": true\n} \n                 Or you can pass  \"replace\": true  to replace any rows with conflicting primary keys with the new values. This requires the  update-row  permission. \n                 Pass  \"alter: true  to automatically add any missing columns to the table. This requires the  alter-table  permission.", "breadcrumbs": "[\"JSON API\", \"The JSON write API\"]", "references": "[]"}, {"id": "json_api:tablesetcolumntypeview", "page": "json_api", "ref": "tablesetcolumntypeview", "title": "Setting a column type", "content": "To set a column type for a table column, make a  POST  to  /<database>/<table>/-/set-column-type . This requires the  set-column-type  permission. \n                 POST /<database>/<table>/-/set-column-type\nContent-Type: application/json\nAuthorization: Bearer dstok_<rest-of-token> \n                 {\n    \"column\": \"title\",\n    \"column_type\": {\n        \"type\": \"email\"\n    }\n} \n                 This will return a  200  response like this: \n                 {\n    \"ok\": true,\n    \"database\": \"data\",\n    \"table\": \"posts\",\n    \"column\": \"title\",\n    \"column_type\": {\n        \"type\": \"email\",\n        \"config\": null\n    }\n} \n                 To provide column type configuration, include a  config  object: \n                 {\n    \"column\": \"title\",\n    \"column_type\": {\n        \"type\": \"url\",\n        \"config\": {\n            \"max_length\": 200\n        }\n    }\n} \n                 To clear an existing column type assignment, set  column_type  to  null : \n                 {\n    \"column\": \"title\",\n    \"column_type\": null\n} \n                 This API stores the assignment in Datasette's internal database, so it can be used with immutable databases as well as mutable ones. \n                 Any errors will use the  standard error format , with a  400  status code for a bad input or a  403  status code for an authentication or permission error.", "breadcrumbs": "[\"JSON API\", \"The JSON write API\"]", "references": "[]"}, {"id": "json_api:tableupsertview", "page": "json_api", "ref": "tableupsertview", "title": "Upserting rows", "content": "An upsert is an insert or update operation. If a row with a matching primary key already exists it will be updated - otherwise a new row will be inserted. \n                 The upsert API is mostly the same shape as the  insert API . It requires both the  insert-row  and  update-row  permissions. \n                 It also accepts the same  binary value JSON format . \n                 POST /<database>/<table>/-/upsert\nContent-Type: application/json\nAuthorization: Bearer dstok_<rest-of-token> \n                 {\n    \"rows\": [\n        {\n            \"id\": 1,\n            \"title\": \"Updated title for 1\",\n            \"description\": \"Updated description for 1\"\n        },\n        {\n            \"id\": 2,\n            \"description\": \"Updated description for 2\",\n        },\n        {\n            \"id\": 3,\n            \"title\": \"Item 3\",\n            \"description\": \"Description for 3\"\n        }\n    ]\n} \n                 Imagine a table with a primary key of  id  and which already has rows with  id  values of  1  and  2 . \n                 The above example will: \n                 \n                     \n                         Update the row with  id  of  1  to set both  title  and  description  to the new values \n                     \n                     \n                         Update the row with  id  of  2  to set  title  to the new value -  description  will be left unchanged \n                     \n                     \n                         Insert a new row with  id  of  3  and both  title  and  description  set to the new values \n                     \n                 \n                 Similar to  /-/insert , a  row  key with an object can be used instead of a  rows  array to upsert a single row. \n                 If successful, this will return a  200  status code and a  {\"ok\": true}  response body. This is deliberately different from the  201  returned by  insert : an upsert may update existing rows without creating anything, so it does not claim resource creation. \n                 Add  \"return\": true  to the request body to return full copies of the affected rows after they have been inserted or updated: \n                 {\n    \"rows\": [\n        {\n            \"id\": 1,\n            \"title\": \"Updated title for 1\",\n            \"description\": \"Updated description for 1\"\n        },\n        {\n            \"id\": 2,\n            \"description\": \"Updated description for 2\",\n        },\n        {\n            \"id\": 3,\n            \"title\": \"Item 3\",\n            \"description\": \"Description for 3\"\n        }\n    ],\n    \"return\": true\n} \n                 This will return the following: \n                 {\n    \"ok\": true,\n    \"rows\": [\n        {\n            \"id\": 1,\n            \"title\": \"Updated title for 1\",\n            \"description\": \"Updated description for 1\"\n        },\n        {\n            \"id\": 2,\n            \"title\": \"Item 2\",\n            \"description\": \"Updated description for 2\"\n        },\n        {\n            \"id\": 3,\n            \"title\": \"Item 3\",\n            \"description\": \"Description for 3\"\n        }\n    ]\n} \n                 When using upsert you must provide the primary key column (or columns if the table has a compound primary key) for every row, or you will get a  400  error: \n                 {\n    \"ok\": false,\n    \"error\": \"Row 0 is missing primary key column(s): \\\"id\\\"\",\n    \"errors\": [\n        \"Row 0 is missing primary key column(s): \\\"id\\\"\"\n    ],\n    \"status\": 400\n} \n                 If your table does not have an explicit primary key you should pass the SQLite  rowid  key instead. \n                 Pass  \"alter: true  to automatically add any missing columns to the table. This requires the  alter-table  permission.", "breadcrumbs": "[\"JSON API\", \"The JSON write API\"]", "references": "[]"}], "truncated": false}