kamiPay LogokamiPay Docs

Authentication

Learn how to authenticate with the kamiPay API

All our endpoints are authenticated using JWT token standard. A specific endpoint, /auth/token, gives you a new token when you need one. A token lasts one day by default, set per credential. Custom durations can be evaluated on a case by case basis.

You will be provided a username and a password that will be used to retrieve tokens. Tokens will then be used as bearer token for authentication through all the endpoints that you have been given access to: every request carries Authorization: Bearer <access_token>.

Getting an Access Token

Request a token with POST /auth/token. Send username and password as form fields (application/x-www-form-urlencoded), not as a JSON body.

const url = "baseURL/auth/token";

// Create the form fields, sent as application/x-www-form-urlencoded
const body = new URLSearchParams({
  username: "YOUR_USERNAME",
  password: "YOUR_PASSWORD"
});

const response = await fetch(url, {
  method: "POST",
  body
});
import requests

url = "baseURL/auth/token"

# Create a dictionary for the form data
data = {
    "username": "YOUR_USERNAME",
    "password": "YOUR_PASSWORD"
}

response = requests.post(url, data=data)
package main

import (
  "bytes"
  "encoding/json"
  "fmt"
  "net/http"
  "net/url"
  "strings"
)

func main() {
  apiURL := "baseURL/auth/token"

  // Create form data
  formData := url.Values{}
  formData.Set("username", "YOUR_USERNAME")
  formData.Set("password", "YOUR_PASSWORD")

  // Make the request
  resp, err := http.Post(
    apiURL,
    "application/x-www-form-urlencoded",
    strings.NewReader(formData.Encode()),
  )

  if err != nil {
    fmt.Println("Error making request:", err)
    return
  }
  defer resp.Body.Close()

  // Process the response
  var result map[string]interface{}
  json.NewDecoder(resp.Body).Decode(&result)

  fmt.Println("Access Token:", result["access_token"])
}

/auth/token is rate-limited per IP address, currently to 10 requests per minute. Beyond that, it returns 429.

  • Request a token once and reuse it until shortly before its expires_at, instead of requesting one per call.
  • Services that reach kamiPay from the same IP, behind one NAT for example, share the limit. Share the token too: keep it in one cache they all read.

Response

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrYW1pcGF5LWJhY2tlb",
  "token_type": "bearer",
  "expires_at": 1785421930
}

The username or the password is wrong. detail names the username you sent, and is the same whichever of the two is wrong:

{
  "detail": "<username> not authenticated"
}

The credentials weren't sent as form fields. A JSON body gets this response even when it carries username and password: here, "Field required" means the wrong encoding, not a missing field. Send them as form fields, as the samples above do.

{
  "detail": [
    {
      "type": "missing",
      "loc": ["body", "username"],
      "msg": "Field required",
      "input": null
    },
    {
      "type": "missing",
      "loc": ["body", "password"],
      "msg": "Field required",
      "input": null
    }
  ]
}

More than 10 token requests in a minute from your IP. Wait a minute before requesting again, and reuse the token you get until shortly before its expires_at. detail states the limit:

{
  "detail": "10 per 1 minute"
}

Response fields

FieldTypeDescription
access_tokenstringThe JWT to send as a bearer token on every authenticated request.
token_typestringAlways bearer.
expires_atintegerUnix timestamp, in seconds, at which the token expires. Same value as the exp claim inside the JWT.

Use expires_at to refresh the token before it expires, instead of decoding the JWT or hardcoding a duration: token duration depends on your credential, so it is not the same for every integration.

Using the Token

After obtaining the access token, include it in the Authorization header for all API requests:

Authorization: Bearer YOUR_ACCESS_TOKEN

On this page