API Quickstart

Goal
A live, self-improving classification endpoint, built entirely from the command line. No console steps, no training data, no model configuration.
Time
~5 minutes

This is the code-first path. If you’d rather see the same loop in the UI first, the Spam Classifier tutorial covers the same ground through the console.

You need a Nyckel account and a clientId / clientSecret pair. Both are in the Access Management section of your account, and on the Integrate tab of any function.


1. Get an access token

Every API request carries a bearer token. Tokens come from connect/token, which sits outside the /v1/ API surface.

curl -X POST 'https://www.nyckel.com/connect/token' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=client_credentials&client_id=<clientId>&client_secret=<clientSecret>'
{
  "access_token": "<accessToken>",
  "token_type":   "Bearer",
  "expires_in":   3600
}

Tokens are valid for one hour. Request a new one when it expires — see Authentication for rotation details.

export NYCKEL_TOKEN='<accessToken>'

2. Create a function

A function is the whole unit: endpoint, model, sample store, and review queue. You pick its input modality (Text, Image, or Tabular) and its output modality at creation time. Both are immutable — a function that classifies text can’t later be switched to images.

curl -X POST 'https://www.nyckel.com/v1/functions' \
  -H "Authorization: Bearer $NYCKEL_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "name":   "sms-spam-classifier",
    "input":  "Text",
    "output": "Classification"
  }'
{
  "id":     "<functionId>",
  "name":   "sms-spam-classifier",
  "input":  "Text",
  "output": "Classification"
}
export NYCKEL_FN='<functionId>'

3. Define the labels

Labels are the decision space — the set of answers the function is allowed to give. Create one call per label.

curl -X POST "https://www.nyckel.com/v1/functions/$NYCKEL_FN/labels" \
  -H "Authorization: Bearer $NYCKEL_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "name": "Spam" }'

curl -X POST "https://www.nyckel.com/v1/functions/$NYCKEL_FN/labels" \
  -H "Authorization: Bearer $NYCKEL_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "name": "Not spam" }'

That’s the whole setup. You have not uploaded a single training example, and the function is already callable.


4. Invoke it

curl -X POST "https://www.nyckel.com/v1/functions/$NYCKEL_FN/invoke?externalId=msg-001" \
  -H "Authorization: Bearer $NYCKEL_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "data": "WINNER!! Claim your prize now, text CLAIM to 80086" }'
{
  "labelName":  "Spam",
  "labelId":    "<labelId>",
  "confidence": 0.94
}

This first prediction is zero-shot — it comes from a foundation model reasoning about your label names, not from a model trained on your data. For plenty of use cases the zero-shot accuracy is good enough to ship on. See How functions work for what happens underneath.

Two parameters worth knowing now:

Parameter Why it matters
externalId Your own id for this input. It comes back on the response and lets you find the captured sample later, without storing anything Nyckel-specific.
capture Defaults to true. Every invoke is captured as a sample you can review. Set capture=false for traffic you don’t want in the review queue.

confidence is a calibrated 0.01.0 score. It’s what you branch on in production — see Tuning confidence thresholds.


5. Correct it

This is the step that makes a function improve. Find the captured sample by the externalId you sent, then set its annotation.

curl "https://www.nyckel.com/v1/functions/$NYCKEL_FN/samples?externalId=msg-001" \
  -H "Authorization: Bearer $NYCKEL_TOKEN"
[
  {
    "id":         "<sampleId>",
    "externalId": "msg-001",
    "data":       "WINNER!! Claim your prize now, text CLAIM to 80086",
    "prediction": { "labelId": "<labelId>", "confidence": 0.94 }
  }
]
curl -X PUT "https://www.nyckel.com/v1/functions/$NYCKEL_FN/samples/<sampleId>/annotation" \
  -H "Authorization: Bearer $NYCKEL_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "labelName": "Not spam" }'

Annotations feed the retraining pipeline. Nyckel retrains as they accumulate, benchmarks the new model against the current one, and promotes it only if it scores better. You don’t trigger any of that.


The loop, in one picture

  1. 1POST /invoke
  2. 2labelName + confidence
  3. 3your application acts
  4. 4PUT /annotation
  5. 5model retrains
Production loop

Steps 4 and 5 are the whole production pattern. Everything else in the API — sample sets, function summaries, field management — exists to support that loop at scale.


Finding samples worth reviewing

In production you won’t annotate by externalId. You’ll pull the samples the model was least sure about and review those first:

# Captured but not yet annotated
curl "https://www.nyckel.com/v1/functions/$NYCKEL_FN/samples?annotated=false&count=20" \
  -H "Authorization: Bearer $NYCKEL_TOKEN"

# Samples where the model's prediction disagreed with the annotation
curl "https://www.nyckel.com/v1/functions/$NYCKEL_FN/samples?predictionAndAnnotationAgree=false" \
  -H "Authorization: Bearer $NYCKEL_TOKEN"

List Samples documents the full filter set.


Next