Get started with zot

zot connects R to the Zotero Web API and local Zotero data sources. This article uses an in-memory request performer and does not contact an external service.

Configure API access

zotConfig() accepts credentials explicitly or reads ZOTERO_LIBRARY_ID and ZOTERO_API_KEY from the process environment. The same interface can be used from interactive R, scripts, automated jobs, and agent-driven workflows. A non-interactive caller reads no shell profile, so an Rscript job takes those variables from ~/.Renviron, the file R reads at startup in every session.

library(data.table)
library(zot)

Config <- zotConfig(userID = "42", key = "example-key")
Config
#> <zotConfig> userID: 42 | key: <redacted> | api: https://api.zotero.org

The configured identifier represents the personal user library. Numeric group identifiers are supplied explicitly where a function accepts library.

zotLibraryPath(Config, library = "user")
#> [1] "/users/42"
zotLibraryPath(Config, library = "5107760")
#> [1] "/groups/5107760"

Read API resources

zotGet() performs one request. zotGetAll() follows Zotero pagination headers. The performer below returns three records over two pages.

Performer <- function(config, method, path, query, body, version) {
  Page <- if (query$start == 0L) {
    list(list(key = "A", title = "One"), list(key = "B", title = "Two"))
  } else {
    list(list(key = "C", title = "Three"))
  }
  list(status = 200L, headers = list(`total-results` = "3"), body = Page)
}

Config$performer <- Performer
Items <- zotGetAll(
  Config,
  path = paste0(zotLibraryPath(Config), "/items/top"),
  limit = 2L
)
vapply(Items, function(Item) Item$key, character(1L))
#> [1] "A" "B" "C"

The default transport returns response status, headers, and parsed content. Patch requests include the supplied item version, and retry handling is limited to the response classes documented by zotRequest().

Use local and semantic data

zotLocalQuery() opens the local SQLite database in read-only mode. zotLocalItems() returns a compact item table and excludes records in Trash. Local data can lag the Web API when the desktop client has not synchronized.

ztSemantic() queries an optional Chroma index through the companion Python environment. It does not create or refresh the index. See vignette("external-integrations", package = "zot") for its runtime requirements.

Build a plan

Plan builders return data without applying the represented changes. In this example, ztRetitle() includes only the title changed by the transformation.

Candidates <- data.table(
  itemKey = c("A", "B"),
  library = "user",
  title = c("Reviewed book (1).pdf", "Existing title")
)

Plan <- ztRetitle(Candidates, transform = ztCleanTitle)
Plan
#>    action library itemKey   payload
#>    <char>  <char>  <char>    <list>
#> 1:  patch    user       A <list[1]>

zotPreview() validates the plan, copies its rows, and stores a digest and summary.

Preview <- zotPreview(Plan, describe = "Normalize one reviewed title")
Preview
#> <zotPreview> Normalize one reviewed title 
#>   digest: 1737f2bbf3c2706d92827a746c5299a38e518e066d343dd596632c04ae8abb9e 
#>    action library     N
#>    <char>  <char> <int>
#> 1:  patch    user     1

zotApprove() creates a record for the preview digest. zotExecute() can then apply the rows and maintain a resumable ledger. These functions do not establish who reviewed a batch; authorization policy belongs to the calling application.

Continue with vignette("guarded-workflow", package = "zot") for an offline execution example and vignette("curation-plans", package = "zot") for the other plan builders.