For developers

Public API guide (for developers)

Created: Updated:

With the public API, an e-commerce site, core system or RPA tool can add a record to a kintone app that has PRING, create a document with that app's PRING template, and get the file, all over HTTPS. It is available on the Pro plan. Issue a PRING API key on the API tab of the settings screen, register a kintone API token for each app, then send the key in the Authorization: Bearer header.

The public API is available on the Pro plan.

Overview and requirements

The PRING public API is an HTTPS API that lets other systems use PRING’s document output without opening kintone. It has three main uses:

  • Add a record to a kintone app from another system, and create a document (PDF, Excel or Word) from it in the same call
  • Create a document from a record that is already in kintone
  • Get the file of a created document

Before you start, check that you have all three of the following:

  1. A Pro plan contract. You can check your plan on the Plans & pricing tab of the settings screen (Plans and pricing).
  2. PRING installed in the kintone app you call, with the template you use registered. Documents are created with that app’s PRING template (Register a template).
  3. A kintone API token registered for each app on the API tab (under Automation & links) of the settings screen. Create the token under “API Token” in the kintone app settings. Paste it into “kintone API token of this app” on the API tab and press Save token; it then shows Registered (date). To remove it, press Delete token, and when “Press again to delete” appears, press it again. Give it only three permissions: Add records, Edit records and View records. PRING stores the token encrypted and uses it only to add and get records.

The API base URL is https://api.pring.tech/api/v1/. Requests and responses are JSON (UTF-8). Only document files are returned as binary.

Authentication

The public API identifies the caller with a PRING API key. This is not the same as the kintone API token (requirement 3). PRING uses the kintone token to call kintone. Your system uses the PRING API key to call PRING.

Issue an API key

  1. In kintone, open the plugin settings of an app that has PRING.
  2. Choose Automation & links on the top row and open the API tab. On plans other than Pro, only “The public API is available on the Pro plan.” and Open Plans & pricing appear, and you cannot issue keys.
  3. Under API keys, press Issue a key and enter a name that shows what it is for (for example, “EC integration live”).
  4. Under Scopes, choose the ones it needs (see the table below). Choose only the ones you need.
  5. For development and testing, select Make it a test key (prk_test_).
  6. If needed, enter Allowed IP addresses, one IP address or CIDR per line (optional, up to 20; see “IP address restriction” below).
  7. Press Issue. The key is shown only once. Press Copy, store it as described in “Storing keys” below, then press Close. After you close it, it cannot be shown again.

Issued keys are listed in the API keys table with name (the key shows only prk_live_… and the last 4 characters), kind, scopes, allowed IPs, issued date, last used date and status. You can have up to 20 active keys. If you use a separate key for each connected system, revoking one key does not stop the other integrations.

Key format

Type Prefix Where to use
Live prk_live_ Production integrations
Test prk_test_ Development and testing environments

A key is a single-line string: prk_live_ or prk_test_ followed by 64 hexadecimal digits. The prefix tells you whether a key is live or test, so check it when you look through logs or review your secrets.

Sending the key

Send the key in the Authorization header of every request, after Bearer.

GET /api/v1/apps HTTP/1.1
Host: api.pring.tech
Authorization: Bearer prk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  • Do not put the key in the URL query (such as ?key=). It is not accepted. URLs are kept in access logs and browser history.
  • Only HTTPS is accepted. HTTP is not.

Scopes

Each key has the operations it may use (scopes). If you call an operation the key does not have, you get 403.

Scope What it allows APIs
records:write Add, update and get records in kintone apps POST PUT GET /api/v1/apps/{appID}/records…
render Create documents and check the status of asynchronous jobs …/records/{recordID}/render, ?render=, GET /api/v1/jobs/{jobID}
files:read Get generated files GET /api/v1/files/{fileID}

The list of apps and templates (GET /api/v1/apps) can be called with a key of any scope. An integration that adds records and gets the PDF needs all three. An integration that only creates documents and does not add records needs only render and files:read.

401 vs 403 and how to fix them

Response Meaning Common causes Fix
401 Unauthorized The caller cannot be identified No Authorization header, Bearer missing, key copied wrongly (spaces or line breaks before or after), revoked key Check the header format and copy the key again. If the key was revoked, issue a new one
403 Forbidden The caller is known, but the operation is not allowed Missing scope, a plan other than Pro, a call from an IP address that is not allowed, no kintone API token registered for that app The response detail gives the reason. Issue a key with the scope added, check the plan, allow the IP address, or register the token on the API tab

Remember: 401 is a problem with the key itself; 403 means the key is correct but there is a problem with permissions or settings. Retrying a 401 with the same key does not fix it.

Storing keys

  • Treat a key like a password. Do not write it in source code, Git repositories, chat or email.
  • Store it in the secret management of the connected system (a cloud secret manager, CI secrets, environment variables and so on), and have your program read it from there.
  • Do not write keys or the Authorization header to application logs, error records or monitoring tools. If you must record a key, record only the prk_live_ prefix and the last few characters.
  • When the person in charge changes, or when you stop an integration, revoke that key.

Revoke and reissue

  • Revoke: In the API keys table on the API tab, press Revoke for the key, and when “Press again to revoke” appears, press it again. “Revoked. This key no longer works.” appears and the status becomes Revoked. A revoked key returns 401 from that moment. This cannot be undone.
  • Reissue (rotate): To switch keys without stopping the integration, (1) issue a new key, (2) change the secret in the connected system to the new key, (3) check that it works with the new key, and (4) revoke the old key. You can use more than one key at the same time, so the integration keeps running until step (3).
  • If a key may have leaked: Revoke it right away. Then issue a new key and switch to it.

IP address restriction (optional)

You can set up to 20 IP addresses or CIDR ranges that may call with a key. Without them the key shows Any. With them, calls from any other address return 403. For server-to-server integrations that call from fixed IP addresses, this limits the damage if the key leaks.

Examples

The examples read the key from the environment variable PRING_API_KEY. Replace the app number 19, the template tpl_invoice and the kintone field codes (such as customer_name) with the ones in your app. You can check app numbers and template IDs with the list in e) below.

Send record values in the same format as the kintone REST API ({"field_code": {"value": value}}).

Ready-to-run samples

These samples run the whole flow: add a record, create a PDF, and download it.

The samples read these environment variables:

Variable What it is Default
PRING_API_KEY Your PRING API key (required) None
PRING_API_BASE API base URL https://api.pring.tech
PRING_APP App number 19
PRING_TEMPLATE Template ID tpl_invoice
PRING_OUT Output file name invoice.pdf

a) Add a record and create the PDF in one call

Add ?render=<template ID>&format=pdf to POST /api/v1/apps/{appID}/records to create the document right after the record is added. You can choose pdf, xlsx or docx as the format (the formats the template supports). The required scopes are records:write and render.

curl

curl -sS -X POST "https://api.pring.tech/api/v1/apps/19/records?render=tpl_invoice&format=pdf" \
  -H "Authorization: Bearer $PRING_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "record": {
      "customer_name": { "value": "Sample Trading Co." },
      "invoice_date": { "value": "2026-10-31" },
      "items": { "value": [
        { "value": { "item_name": { "value": "Item A" }, "qty": { "value": "2" }, "unit_price": { "value": "10000" } } }
      ] }
    }
  }'

Node.js (fetch)

const res = await fetch("https://api.pring.tech/api/v1/apps/19/records?render=tpl_invoice&format=pdf", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PRING_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    record: {
      customer_name: { value: "Sample Trading Co." },
      invoice_date: { value: "2026-10-31" },
    },
  }),
});
if (!res.ok) throw new Error(`${res.status} ${(await res.json()).detail}`);
const { id, file } = await res.json();
console.log("record", id, "file", file.id);

Python (requests)

import os, requests

res = requests.post(
    "https://api.pring.tech/api/v1/apps/19/records",
    params={"render": "tpl_invoice", "format": "pdf"},
    headers={"Authorization": f"Bearer {os.environ['PRING_API_KEY']}"},
    json={"record": {"customer_name": {"value": "Sample Trading Co."}, "invoice_date": {"value": "2026-10-31"}}},
    timeout=60,
)
res.raise_for_status()
body = res.json()
print("record", body["id"], "file", body["file"]["id"])

Example response (201 Created):

{
  "id": "105",
  "revision": "1",
  "file": {
    "id": "fil_01HXY...",
    "url": "https://api.pring.tech/api/v1/files/fil_01HXY...",
    "expires_at": "2026-10-31T09:15:00Z"
  }
}

id is the kintone record number. Get the file as described in c). Without ?render=, only the record is added, and the response has only id and revision. To update a record, use PUT /api/v1/apps/{appID}/records/{recordID}. To get one, use GET /api/v1/apps/{appID}/records/{recordID}. When you update, you can send revision. kintone checks it, and if the record has changed in the meantime, you get 409.

Add several records at once

Put an array in records instead of record to add up to 100 records in one call. 101 or more returns 413.

{
  "records": [
    { "customer_name": { "value": "Sample Trading Co." }, "invoice_date": { "value": "2026-10-31" } },
    { "customer_name": { "value": "Example Industries" }, "invoice_date": { "value": "2026-10-31" } }
  ]
}

Without ?render=, the response is 201 with the record numbers and revisions, in the order you sent them.

{
  "ids": ["105", "106"],
  "revisions": ["1", "1"]
}

With records and ?render=, the documents are created asynchronously, and the response is 202 with a job_id (see d).

b) Create a document from an existing record

Send the template (template) and format (format, optional) to POST /api/v1/apps/{appID}/records/{recordID}/render. If you put the field code of a kintone attachment field in attach_to_field, the file is also saved in that record’s attachment field. The required scope is render.

curl

curl -sS -X POST "https://api.pring.tech/api/v1/apps/19/records/105/render" \
  -H "Authorization: Bearer $PRING_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "template": "tpl_invoice", "format": "pdf", "attach_to_field": "invoice_pdf" }'

Node.js (fetch)

const res = await fetch("https://api.pring.tech/api/v1/apps/19/records/105/render", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.PRING_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({ template: "tpl_invoice", format: "pdf", attach_to_field: "invoice_pdf" }),
});
const { file, attached_to, warnings } = await res.json();

Python (requests)

res = requests.post(
    "https://api.pring.tech/api/v1/apps/19/records/105/render",
    headers={"Authorization": f"Bearer {os.environ['PRING_API_KEY']}"},
    json={"template": "tpl_invoice", "format": "pdf", "attach_to_field": "invoice_pdf"},
    timeout=60,
)
file = res.json()["file"]

Example response (201 Created):

{
  "file": {
    "id": "fil_01HXY...",
    "url": "https://api.pring.tech/api/v1/files/fil_01HXY...",
    "expires_at": "2026-10-31T09:15:00Z"
  },
  "attached_to": "invoice_pdf",
  "warnings": []
}

file has the same format as in a). When you send attach_to_field, attached_to has the field code of the attachment field the file was saved in. This API returns the result with 201 right away. It does not return 202.

Record API attachments in the file log

To record files that the API saves in attachment fields in the File Log app as well, register a File log API token on the API tab.

  1. In the File Log app, create a token under App settings > API Token. Give it only the Add records permission.
  2. Paste it into File log API token on the API tab and press Save log token.

Until you register it, the API tab shows “File log token not registered (API attachments are not recorded)”. If you do not have a File Log app yet, create one on the Audit log tab first.

If you send attach_to_field without this token, the file is saved in the attachment field, but warnings has "audit_not_recorded". In that case, the File Log app has no row for that file.

c) Get the file

Call GET /api/v1/files/{fileID} and you are redirected with 302 to a one-time link (/api/v1/files/{fileID}/content?t=…). Get the file from that link. The link does not need the Authorization header. The required scope is files:read.

  • The one-time link expires after 15 minutes. The generated file is also deleted 15 minutes after it is generated.
  • A used or expired link returns 410. To download again, call GET /api/v1/files/{fileID} again, before the file is deleted.
  • To keep a document, save it in kintone with attach_to_field in b).

curl (-L follows the redirect)

curl -sS -L -o invoice.pdf "https://api.pring.tech/api/v1/files/fil_01HXY..." \
  -H "Authorization: Bearer $PRING_API_KEY"

Node.js (fetch) (fetch follows redirects automatically)

import { writeFile } from "node:fs/promises";

const res = await fetch(`https://api.pring.tech/api/v1/files/${file.id}`, {
  headers: { Authorization: `Bearer ${process.env.PRING_API_KEY}` },
});
if (!res.ok) throw new Error(`${res.status}`);
await writeFile("invoice.pdf", Buffer.from(await res.arrayBuffer()));

Python (requests) (requests also follows redirects automatically)

res = requests.get(
    f"https://api.pring.tech/api/v1/files/{file['id']}",
    headers={"Authorization": f"Bearer {os.environ['PRING_API_KEY']}"},
    timeout=60,
)
res.raise_for_status()
open("invoice.pdf", "wb").write(res.content)

d) Asynchronous: 202 and job_id

Only when you send records with ?render= does the API return 202 Accepted and a job_id instead of the documents. The records are already added at this point, so ids and revisions are also returned. Adding one record (a) and creating a document from an existing record (b) never return 202.

{
  "job_id": "job_01HXY...",
  "status": "queued",
  "poll_url": "/api/v1/jobs/job_01HXY...",
  "ids": ["105", "106"],
  "revisions": ["1", "1"]
}

Call GET /api/v1/jobs/{jobID} at intervals until status is done or failed. The required scope is render.

status moves from queued (waiting) to running (processing) to done (finished) or failed (failed). A job has these items:

  • total: the number of documents. done: how many are finished. failed: how many failed.
  • detail: the reason, when status is failed.
  • result_url: set when the job is done. A signed URL of one ZIP that has every document. It works for 24 hours.
  • files: the same ZIP, as [{"url": …, "expires_at": …}].
  • expires_at: when it expires.

Example of a finished job:

{
  "status": "done",
  "total": 2,
  "done": 2,
  "failed": 0,
  "result_url": "https://...",
  "files": [
    { "url": "https://...", "expires_at": "2026-11-01T09:00:00Z" }
  ],
  "expires_at": "2026-11-01T09:00:00Z"
}

curl

curl -sS "https://api.pring.tech/api/v1/jobs/job_01HXY..." -H "Authorization: Bearer $PRING_API_KEY"

Node.js (fetch)

async function waitJob(jobId) {
  for (let i = 0; i < 60; i++) {
    const res = await fetch(`https://api.pring.tech/api/v1/jobs/${jobId}`, {
      headers: { Authorization: `Bearer ${process.env.PRING_API_KEY}` },
    });
    const job = await res.json();
    if (job.status === "done") return job.result_url;
    if (job.status === "failed") throw new Error(job.detail ?? "job failed");
    await new Promise((r) => setTimeout(r, Math.min(2000 * 2 ** Math.floor(i / 5), 30000)));
  }
  throw new Error("timeout");
}

Python (requests)

import time

def wait_job(job_id):
    for i in range(60):
        job = requests.get(
            f"https://api.pring.tech/api/v1/jobs/{job_id}",
            headers={"Authorization": f"Bearer {os.environ['PRING_API_KEY']}"},
            timeout=30,
        ).json()
        if job["status"] == "done":
            return job["result_url"]
        if job["status"] == "failed":
            raise RuntimeError(job.get("detail", "job failed"))
        time.sleep(min(2 * 2 ** (i // 5), 30))
    raise TimeoutError(job_id)

Start polling at 2 seconds and make the interval longer step by step (up to 30 seconds in the examples). If you keep calling at short intervals, you hit the rate limit (429).

e) List apps and templates

GET /api/v1/apps returns the apps that have PRING and the templates of each app. Use it to check app numbers and template IDs.

curl

curl -sS "https://api.pring.tech/api/v1/apps" -H "Authorization: Bearer $PRING_API_KEY"

Node.js (fetch)

const res = await fetch("https://api.pring.tech/api/v1/apps", {
  headers: { Authorization: `Bearer ${process.env.PRING_API_KEY}` },
});
const { apps } = await res.json();
for (const a of apps) console.log(a.app_id, a.name, a.templates.map((t) => t.id));

Python (requests)

apps = requests.get(
    "https://api.pring.tech/api/v1/apps",
    headers={"Authorization": f"Bearer {os.environ['PRING_API_KEY']}"},
    timeout=30,
).json()["apps"]
for a in apps:
    print(a["app_id"], a["name"], [t["id"] for t in a["templates"]])

Example response:

{
  "apps": [
    {
      "app_id": "19",
      "name": "Invoices",
      "api_token_registered": true,
      "templates": [
        { "id": "tpl_invoice", "name": "Invoice", "formats": ["pdf", "xlsx"] }
      ]
    }
  ]
}

For an app where api_token_registered is false, you cannot add or get records until you register a kintone API token on the API tab.

Errors

Errors are returned as JSON in the RFC 7807 (Problem Details) format (Content-Type: application/problem+json). detail tells you what to fix.

{
  "type": "https://pring.tech/en/help/developer/public-api#errors",
  "title": "Forbidden",
  "status": 403,
  "detail": "This API key does not have the render scope.",
  "instance": "/api/v1/apps/19/records/105/render"
}

When kintone does not accept the record values, the body can also have errors: kintone’s messages for each field code ({"field_code": ["message", ...]}).

Status Meaning Fix
400 Bad Request The JSON is malformed, a required item is missing, or the format is wrong Fix the item named in detail and send again
401 Unauthorized The key is missing, wrong or revoked See “401 vs 403 and how to fix them”
403 Forbidden Scope, plan, IP restriction, or no kintone API token registered See “401 vs 403 and how to fix them”
404 Not Found The app, record, template, file or job does not exist (files are deleted after 15 minutes) Check the numbers and IDs with the list in e)
409 Conflict The record has changed (the revision sent with PUT does not match the kintone record) Get the record again and send it with the latest revision
413 Payload Too Large records has 101 or more records Split them into requests of 100 or fewer
422 Unprocessable Content kintone did not accept the record values (a required field is empty, a value is not one of the options, and so on), or the request is over your plan’s per-request limit Check the kintone field and reason in detail and errors, and fix the value. If you are over the limit, split the request
429 Too Many Requests Rate limit, or this month’s output page cap has been reached Wait for the seconds in the Retry-After header, then send again. The page cap is shown in detail (Usage limits and page counting)
502 Bad Gateway kintone did not answer Wait and send again
500, 503 A temporary failure on the PRING or kintone side Wait and send again. If it continues, contact support with the instance and the time

You may retry 429, 502 and 5xx. A 429 response has a Retry-After header (in seconds). Wait that long before you retry. For 400, 401, 403, 404, 409, 413 and 422, sending the same request again gives the same result. Before you retry adding a record (POST …/records), check whether the earlier attempt already added it (to avoid duplicate records).

Limits

  • Records per request: One record per request, or up to 100 in records. 101 or more returns 413. Going over your plan’s per-request limit returns 422.
  • Rate limit: Up to 100 requests per second per API key. There is also a limit per contract (tenant). Above it, you get 429.
  • Pages: Pages created through the API count toward the 100,000 pages included in Pro each month. They are counted the same way as output from the screen (Usage limits and page counting).
  • File retention: Generated files are deleted after 15 minutes. The job ZIP (result_url) works for 24 hours. To keep a file, save it in kintone with attach_to_field.
  • File log: To record API attachments in the File Log app, register a File log API token on the API tab (see b). Without it, warnings has audit_not_recorded.
  • Record values: They are not stored on PRING’s servers (they are handled only in memory while processed).
  • Calls that look like misuse, such as a sudden rise in calls, may be stopped for a time.

Frequently asked questions

Can I use it on Lite or Standard?

No. The public API is available on the Pro plan (including Enterprise and contracts through a reseller). On Lite, Standard and the free trial, the API tab of the settings screen says it is available on Pro, and you cannot issue a key.

Do documents created through the API count toward my pages?

Yes. Pages created through the API count toward the 100,000 pages included in Pro each month. Pages above the included amount are added as add-ons, the same as output from the screen.

What permissions should the kintone API token have?

Only three for that app: Add records, Edit records and View records. Do not give it app management or record deletion permissions. PRING stores the token encrypted and uses it only to add and get records.

Are record values kept on PRING's servers?

No. Record values are handled only in memory while they are processed, and are added to kintone. Generated files are kept for a short time so you can get them, and are deleted after 15 minutes by default. To keep a file, send attach_to_field to save it in a kintone attachment field.

I lost my API key. Can I see it again?

No. A key is shown only once, when you issue it. If you lose it, issue a new key, switch to it, and revoke the old key.

How many records can I send at once?

One record per request, or up to 100 in records. 101 or more returns 413. Going over your plan's per-request limit returns 422. If you have more, split them into several requests. When you send records with ?render=, they are accepted asynchronously (202 and a job_id).

The response has audit_not_recorded in warnings. What does it mean?

The file was saved in the kintone attachment field. But no file log API token is registered, so the File Log app has no row for it. To record it, create a token with only the Add records permission under App settings > API Token in the File Log app, paste it into File log API token on the API tab, and press Save log token.

What is the difference between test keys and live keys?

They start with prk_test_ and prk_live_ so you can tell them apart. Use test keys for development and testing, and live keys for production integrations. Do not mix them in the same configuration file.

Written and maintained by: TechJapan LLC (PRING team) (Developer and operator) · Contact