API

Introduction

swagger.png

Everything you can do from the web application is also available through the DEDOMENA REST API, so you can automate anonymization and synthetic-data generation from your own applications and pipelines. Interactive documentation (Swagger) is available at the /docs endpoint of the API.

Authentication. Every request is authorized with your personal token, passed as a query parameter (?token=YOUR_TOKEN). The only exception is the method catalogue, which is public so that a user interface can load it before anyone signs in. Requests are rate-limited per account.

Working asynchronously. Anonymization and training do not finish inside the request. They answer immediately with a runId, and you follow that run with a status endpoint. Nothing is lost if your process dies in between: the run keeps going and the identifier stays valid.

Endpoint reference

Anonymization

EndpointPurpose
GET /nucleus/anonymization_methodsThe catalogue of methods, their parameters, and which ones apply to each kind of asset. No token needed.
GET /nucleus/anonymization_methods/resolveWhich methods are allowed for one specific column or sensitive element, and which is the default.
POST /nucleus/anonymizeAnonymize one or several datasets. Four ways to hand over the data.
GET /nucleus/anonymize/runs/\{run_id\}The status of an anonymization run, and what it produced.
GET /nucleus/download/anonymized/\{run_id\}Temporary download links for the anonymized datasets.
POST /nucleus/anonymize/incrementAnonymize rows that arrived after a delivery, keeping the same equivalences.
POST /nucleus/deanonymizeRestore the original values of specific columns.

Synthetic data

EndpointPurpose
POST /nucleus/synthesizeTrain a synthesizer.
POST /nucleus/generate/\{run_id\}Generate a synthetic dataset from a trained synthesizer.
POST /nucleus/generate/realtime/\{run_id\}Generate a small sample and get the rows back in the response.
GET /nucleus/download/syntheticdata/\{run_id\}Download a generated dataset as CSV, PARQUET or JSON.
POST /nucleus/uploadmodelUpload a synthesizer trained locally with Nucleus Edge.
GET /nucleus/runs/listList your trained synthesizers.
GET /nucleus/runs/info/\{run_id\}Details and status of a synthesizer.
GET /nucleus/runs/logs/\{run_id\}Training logs of a run.

Discovering the methods

You never have to hard-code the list of methods. Ask for the catalogue:

GET /nucleus/anonymization_methods

json

\{

  "version": "2026.08",

  "methods": \{

    "generalize": \{

      "code": "generalize",

      "name_en": "Generalize", "name_es": "Generalizar",

      "description_en": "Replaces the value with the range it falls into.",

      "param": \{ "name": "bucket", "type": "number", "default": 10, "min": 0.0001 \},

      "enabled": true

    \},

    "hash": \{ "code": "hash", "param": null, "enabled": true \}

  \},

  "rules": \{

    "dataset":  \{ "by_dtype":   \{ "numeric": \{ "allowed": ["generalize", "perturb", "..."], "default": "generalize" \} \} \},

    "image":    \{ "by_element": \{ "face":    \{ "allowed": ["blur", "pixelate", "redact"],   "default": "blur" \} \} \},

    "document": \{ "default_element": \{ "allowed": ["..."], "default": "..." \} \},

    "audio":    \{ "default_element": \{ "allowed": ["beep", "silence", "remove"], "default": "beep" \} \}

  \}

\}

Three things are worth reading from it:

  • methods[code].param is the JSON schema of that method's extra parameter — its name, type, default, and the allowed options when it is a closed list. null means the method takes no parameter.

  • methods[code].enabled tells you whether the engine can actually run it. A disabled method may be listed so a user interface can show it as coming soon, but do not send it.

  • rules resolves which methods make sense where: by column data type for datasets, by sensitive element for images, and a single default for documents and audio.

Two optional query parameters narrow the answer: asset_type (dataset, image, document, audio) trims rules to that one kind, and enabled_only=true drops the methods that cannot run yet.

If you would rather not cross the catalogue yourself, ask for one column or one element and get the answer already resolved:

GET /nucleus/anonymization_methods/resolve?asset_type=dataset&dtype=numeric

GET /nucleus/anonymization_methods/resolve?asset_type=image&element=face

dtype is for datasets (numeric, datetime, string, categorical, boolean); element is for images, documents and audio, and accepts either the code or the display name in English or Spanish.

Writing the configuration

The configuration is the same object whichever way you send the data. It names, for each column, what to do with it:

json

\{

  "anonymizerName": "Employees – supplier copy",

  "anonymizerDescription": "Anonymized for the external analytics team",

  "anonymizerUseCase": "103",

  "datasets": [

    \{

      "datasetName": "employees",

      "columns": \{

        "employee_id": \{ "method": "pseudonym", "param": "EMP-\\d\{6\}" \},

        "first_name":  \{ "method": "simulate", "param": "first_name" \},

        "surnames":    \{ "method": "simulate", "param": "last_names" \},

        "email":       \{ "method": "simulate", "param": "email",

                         "derive_from": ["first_name", "surnames"] \},

        "national_id": \{ "method": "mask", "param": "opaque" \},

        "birth_date":  \{ "method": "date_shift", "param": "employee_id" \},

        "postcode":    \{ "method": "generalize", "param": 5, "type": "string" \},

        "salary":      \{ "method": "perturb", "param": 0.05 \},

        "notes":       \{ "method": "coding", "param": "surrogates" \}

      \}

    \}

  ]

\}

Top-level fields

FieldRequiredMeaning
anonymizerNameyesA name for the delivery. It also names the collection that groups the results.
anonymizerDescriptionyesFree text, for whoever finds this run later.
anonymizerUseCaseyesUse-case code, e.g. "103" for external data sharing.
datasetsyesOne entry per table. A single table is a list of one.
basedOnRunIdnoContinue the equivalences of an earlier run (see Adding rows).
runIdnoPublish your own run identifier instead of letting the API generate it.

Per-dataset fields

FieldMeaning
datasetNameThe name of the table. It is how the configuration is matched to the data you send.
columnsColumn name → what to do with it.
assetIdOnly when the data is already on the platform. Leave it out otherwise: it is resolved for you.
rowsThe data itself, when you send it inline (JSON only).

Per-column fields

FieldMeaning
methodThe method code, from the catalogue. The only required field.
paramThe method's extra parameter. A single value, or an object with named parameters.
references"table.column". Reuses another column's equivalences, so a foreign key gets the same replacement the entity got in its own table.
derive_fromBuilds this value from the already substituted value of another column in the same row. Accepts a list.
group_variantsOn by default. Variants of the same value in a column share one replacement, so Dedomena and Dedomena.AI end up as the same fake company. Set it to false where near-identical values are genuinely different entities.
typeThe column's type — numeric, datetime, string, categorical, boolean — when you do not want it inferred. This is what keeps a postcode like 08001 from being read as the number 8001 and losing its leading zero, which no later step can recover.

The parameter of each method

MethodparamNotes
pseudonymthe pattern, e.g. "EMP-\\d\{6\}"Literals, \d, \w, classes like [A-Z0-9] and repetitions \{n\}. Without a pattern, identifiers are generated freely. Two records never get the same one.
simulatethe provider, e.g. "first_name"Generates a believable value instead of noise. email, phone and url come from reserved ranges, so a generated value never belongs to a real person.
mask"asterisks" or "opaque"Covers the value instead of replacing it.
generalizethe bucket size, e.g. 5Replaces the value with the range it falls into.
perturbnoise from 0.0 to 1.0Keeps the distribution.
date_shiftthe column to stay stable byMoves the date, keeping the same interval for every row of the same entity, so ages and sequences still add up.
coding"tokens", "surrogates" or "invented"Free text: finds the personal data inside and replaces it. surrogates reuses the replacement the same person got in their own column.
text_regenerateDiscards the original text and writes a new one from the record's already substituted values. For fields too dense in personal data to trust detection.
hash, shuffle, drop
blur, pixelate, redactpixelate takes the block sizeImages.
beep, silence, removeAudio.

Columns you do not list are delivered untouched. That is the intended way to keep the fields that carry no personal data.

Relating several tables. A foreign key must receive the same replacement the entity received in its own table, or the data stops joining. Write it with references, using "table.column":

json

\{

  "datasets": [

    \{ "datasetName": "employees",

      "columns": \{ "employee_id": \{ "method": "pseudonym", "param": "EMP-\\d\{6\}" \} \} \},

    \{ "datasetName": "absences",

      "columns": \{ "employee_id": \{ "method": "pseudonym",

                                    "references": "employees.employee_id" \} \} \}

  ]

\}

Because references are written by name, not by identifier, the whole configuration can be authored before anything has been uploaded.

Sending the data — four ways in

POST /nucleus/anonymize?token=YOUR_TOKEN

The same route takes two content types, and your request says which: with application/json the body is the configuration, and the data travels inside it; with multipart/form-data the configuration goes in a configuration field and the datasets are attached as files.

Exactly one of these four per call:

Way inHowWhen
rowsThe data inline, inside each dataset of a JSON body.Thousands of rows, straight from your own application.
filesThe datasets attached to a multipart request.Large tables, or files you already have on disk.
collectionId / collection_idThe identifier of a collection already on the platform.Data you uploaded earlier, several tables at once.
assetId / asset_idThe identifier of a single dataset already on the platform, either as a field of its dataset in the configuration or as a parameter of the call.One table already on the platform.

1 — The data in the body (JSON). The most convenient way from a backend: no multipart, one request.

json

\{

  "anonymizerName": "Employees – supplier copy",

  "anonymizerDescription": "Anonymized for the external analytics team",

  "anonymizerUseCase": "103",

  "dataSource": 232,

  "datasets": [

    \{

      "datasetName": "employees",

      "columns": \{ "first_name": \{ "method": "simulate", "param": "first_name" \} \},

      "rows": [

        \{ "employee_id": "EMP-000001", "first_name": "Ana",  "salary": 30000 \},

        \{ "employee_id": "EMP-000002", "first_name": "Luis", "salary": 41000 \}

      ]

    \}

  ]

\}

bash

curl -X POST "https://\<your-nucleus-api\>/nucleus/anonymize?token=YOUR_TOKEN" \

     -H "Content-Type: application/json" \

     -d @anonymize_config.json

What were form fields become fields of the body: dataSource (231 public domain, 232 proprietary, 233 licensed), and optionally country and useCases.

Inline rows are held whole in memory and travel in a single request, so this way in suits thousands of rows, not millions. Above that, attach the file or upload it first.

2 — The datasets attached (multipart). Each file name without its extension must match a datasetName.

bash

curl -X POST "https://\<your-nucleus-api\>/nucleus/anonymize?token=YOUR_TOKEN" \

     -F "configuration=$(cat anonymize_config.json)" \

     -F "data_source=232" \

     -F "files=@employees.csv" \

     -F "files=@absences.csv"

Accepted formats: csv, txt, tsv, json (an array of objects or JSON Lines), jsonl, parquet, xlsx. Separators are detected, so a semicolon-delimited CSV needs no extra flag.

3 — A collection already on the platform. Nothing is uploaded again; each datasetName is matched against the names of the assets in the collection, and a name that matches nothing is reported before the call returns.

bash

curl -X POST "https://\<your-nucleus-api\>/nucleus/anonymize?token=YOUR_TOKEN" \

     -F "configuration=$(cat anonymize_config.json)" \

     -F "collection_id=COLLECTION_ID"

4 — A single dataset already on the platform. Either put its assetId in its dataset in the configuration, or pass asset_id alongside a configuration with exactly one dataset.

Images, documents and audio are anonymized through ways 3 and 4, after uploading them to the platform; a collection may mix them with tables. For a non-tabular asset the keys of columns are the sensitive elements to treat — face, person, and so on — rather than column names. Use /nucleus/anonymization_methods/resolve to find out which methods each element accepts. Ways 1 and 2 take tabular data only.

The answer comes back straight away:

json

\{ "runId": "6f1c...", "status": "processing" \}

Data that arrives in the request is registered as an asset and grouped into a collection, so the platform treats the set as related, and then profiled — that is why the run reports 10 for a while before the anonymization itself starts.

Following the run

GET /nucleus/anonymize/runs/\{run_id\}?token=YOUR_TOKEN

Answers from the first moment, so there is no window where the identifier you were given is not yet known.

statusMeaning
10The datasets are being registered and profiled.
50Finished. The output is ready to collect.
51Failed.

Once it reaches 50, the answer also lists what was produced, one entry per delivered dataset with its own assetId:

json

\{

  "runId": "6f1c...",

  "status": "50",

  "anonymizedAssets": [

    \{ "assetId": "9ab3...", "name": "employees_anonymized"\}

  ]

\}

Collecting the anonymized data

GET /nucleus/download/anonymized/\{run_id\}?token=YOUR_TOKEN

The API does not stream the data. It signs each object in storage and returns a temporary URL that anyone can GET until it expires, which is how large deliveries are served without turning the API into a data proxy.

json

\{

  "runId": "6f1c...",

  "expiresInMinutes": 60,

  "datasets": [

    \{ "assetId": "9ab3...", "name": "employees_anonymized",

      "url": "https://storage.googleapis.com/...&X-Goog-Signature=..." \}

  ]

\}

ParameterMeaning
asset_idSign only that dataset. By default every dataset of the run is signed.
expires_minutesHow long each link stays valid. One hour by default, one day at most.

The delivered file is a CSV with ; as the separator, every field quoted, and a byte-order mark so that spreadsheet software opens it with the right encoding.

Treat the link as a secret. Its signature grants read access to anonymized personal data until it expires, so do not paste it into a ticket or a chat.

A run that has not finished answers 409 and tells you its status, rather than an empty success.

Adding rows to a delivery already made

POST /nucleus/anonymize/increment?token=YOUR_TOKEN

When new rows arrive after a delivery, you do not re-anonymize everything. Point at the previous run and send only what is new:

bash

curl -X POST "https://\<your-nucleus-api\>/nucleus/anonymize/increment?token=YOUR_TOKEN" \

     -F "run_id=PREVIOUS_RUN_ID" \

     -F "files=@employees_new_rows.csv" \

     -F "files=@absences_new_rows.csv"

The configuration is not sent: it is read from the run being continued. A value that already appeared keeps the replacement it was given, a new identifier never repeats an old one, and the dates of a person already seen move by the same interval.

Two rules are worth knowing before you use it:

  • What was delivered before is not reprocessed. Free text cannot be reproduced — detection runs a model and regeneration writes new prose — so re-running it would change what your recipient already has.

  • Every table of the run needs its file, even one with only a header. A partial increment would leave some tables up to date and others not, with foreign keys pointing at rows that do not exist.

The data source, country and use case are inherited from the original assets, so an increment cannot declare something different from the delivery it belongs to.

Reversing an anonymization

POST /nucleus/deanonymize

Restores the original values of the columns you name, using the equivalence table stored for that asset. Only reversible methods can be reversed, and only while that table is kept — an asset whose equivalences were destroyed cannot be recovered.

json

\{

  "token": "YOUR_TOKEN",

  "assetId": "9ab3...",

  "columns": ["first_name", "surnames"]

\}

Synthesize

Creating synthetic data with the API is a two-step process: first train a synthesizer, then generate data from it.

1. TrainPOST /nucleus/synthesize?algorithm=ALGORITHM&token=YOUR_TOKEN

ALGORITHM is one of generic, transactional, or relational. Provide the asset and the training options; the column types are taken automatically from the analysis, so you do not need to list them.

json

\{

  "assetId": "a1b2c3d4-...",

  "synthesizerName": "customers_v1",

  "synthesizerDescription": "Quality mode",

  "epochs": 200,

  "batchSize": 256,

  "amplify": "quality",

  "constraints": ["age\>=18"]

\}

bash

curl -X POST "https://\<your-nucleus-api\>/nucleus/synthesize?algorithm=generic&token=YOUR_TOKEN" \

     -H "Content-Type: application/json" \

     -d @synthesize_config.json

The response includes a runId and the training status. Training runs in the background; check its progress with the run endpoints below. (You can also train from a connected database by providing a databaseId and tableName instead of an assetId.)

2. Generate — once training has finished, create a synthetic dataset from the trained synthesizer:

POST /nucleus/generate/\{run_id\}?num_rows=100000&token=YOUR_TOKEN

For small, on-demand samples you can use POST /nucleus/generate/realtime/\{run_id\}?num_rows=...&replicate_outliers=yes|no, which returns the rows directly (up to 5,000).

3. Download — retrieve the generated dataset in the format you prefer:

GET /nucleus/download/syntheticdata/\{run_id\}?file_format=CSV&token=YOUR_TOKEN

Available formats: CSV, PARQUET, JSON.

Managing runs

EndpointPurpose
GET /nucleus/runs/listList all your trained synthesizers.
GET /nucleus/runs/info/\{run_id\}Get the details and status of a synthesizer.
GET /nucleus/runs/logs/\{run_id\}Get the training logs of a run.
POST /nucleus/uploadmodelUpload a synthesizer trained locally with Nucleus Edge

These endpoints cover synthesizer runs. An anonymization run is followed with GET /nucleus/anonymize/runs/\{run_id\} instead.

All run endpoints take your token as a query parameter.

API | Dedomena AI Documentation | Dedomena AI