Overview
The Periodic Intel API allows you to programmatically manage your data collection jobs, retrieve generated reports, and configure notifications. The API is a RESTful interface that uses JSON for requests and responses.
Versioning: The current API has no version prefix in the path (e.g.,
/v1/). If breaking changes are introduced in the future, a versioned path will be adopted. You will be notified in advance of any such changes.
Authentication
Most endpoints accept either an API key or a user-session Bearer token.
However, API key management (/api-keys) requires a logged-in user session.
- Open the API Keys screen in your dashboard.
- Click Create to generate a new API Key.
- Make sure to copy the temporary secret (e.g.,
msk_xxxx) shown immediately after creation. You won't be able to view it again.
When using an API key, you can send it in either of these formats:
X-API-Key: msk_your_api_key_here
Authorization: ApiKey msk_your_api_key_here
Inside the dashboard, the web client uses Firebase ID-token Bearer authentication.
Keep your API keys secure. Do not share them in publicly accessible areas such as GitHub, client-side code, etc.
Authentication error behavior:
- If no authentication credential is provided, a
401 Unauthorizedresponse is returned. - If the API key is invalid, disabled, or expired, the current implementation returns
400 Bad Request. - If the key is valid but you do not have permission to access the requested resource (e.g., accessing another user's resource), a
403 Forbiddenresponse is returned.
Base URL
https://api.stratum-flow.com
Error Handling
Common Error Format
When an error occurs, the response body will be in the following format:
{
"ok": false,
"message": "A description of the error"
}
HTTP Status Codes
| Code | Meaning | Common Causes |
|---|---|---|
400 Bad Request |
Invalid request | Missing required fields, validation errors, invalid/disabled API key, some plan-gated option errors |
401 Unauthorized |
Authentication failed | No authentication credential provided |
403 Forbidden |
Access denied | Accessing another user's resource, or hitting endpoints that require Starter or above |
404 Not Found |
Resource not found | The specified job, report, or config ID does not exist |
429 Too Many Requests |
Rate limit exceeded | Sending too many requests in a short period |
500 Internal Server Error |
Server error | Internal failure such as a job enqueueing error |
Endpoints
Jobs
Jobs define the data collection instructions, trigger frequency, and conditions for automatic generation.
GET /jobs
List your configured jobs. Returns 10 items per page (default) with cursor-based pagination.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
cursor |
string | Document ID of the last item on the previous page (value of pagination.nextCursor). Omit to start from the beginning. |
limit |
number | Items per page. Default: 10, Max: 100 |
Response (200 OK):
| Name | Type | Description |
|---|---|---|
ok |
boolean | Indicates if the request was successful |
data |
array | An array of Job Objects |
pagination.nextCursor |
string | null | Cursor for the next page. null means this is the last page. |
pagination.hasMore |
boolean | Whether more items are available |
Example:
{
"ok": true,
"data": [
{
"id": "b0DoFn4h9TKNWHsjdsEd",
"name": "競合SaaSの動向リサーチi",
"sourceUrl": "",
"promptInstruction": "競合・市場・技術トレンドを横断的に調査し、重要な変化とインパクトを要約してください。",
"intervalMinutes": 1440,
"timezone": "UTC",
"status": "paused",
"running": false,
"notificationConfigId": null,
"nextRunAt": "2026-03-29T11:49:00.000Z"
},
/* …etc… */
],
"pagination": {
"nextCursor": "X8ikRFjvXA0U6imnW7X4",
"hasMore": true
}
}
POST /jobs
Create a new job.
Body (JSON):
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Identifier for the job |
promptInstruction |
string | Yes | AI instructions for collection and analysis |
sourceUrl |
string | No | URL of the source (http/https) |
intervalMinutes |
number | No | Run interval in minutes. Default: 60, Minimum: 60 |
timezone |
string | No | Timezone (e.g., Asia/Tokyo). Default: UTC |
researchMode |
string | No | "normal" or "extended". Default: "normal". Extended mode expands search and page traversal for deeper investigation. Each run costs 3x the normal usage. Requires Pro plan or above. |
researchModelProfile |
string | No | "standard" or "precision". Default: "standard". precision prioritizes better factual accuracy and higher report quality, and adds +4 credits on top of the research depth cost. Requires Pro plan or above. |
notificationConfigId |
string | null | No | ID of the notification config (Starter plan or above) |
nextRunAt |
string | No | First scheduled run time (ISO8601). Defaults to the current time (runs immediately). |
Response (200 OK):
| Name | Type | Description |
|---|---|---|
ok |
boolean | true |
data |
object | The newly created Job Object |
PATCH /jobs/:id
Update an existing job's parameters. Only the fields you provide will be updated.
Body (JSON):
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | No | Identifier for the job |
promptInstruction |
string | No | AI instructions for collection and analysis |
sourceUrl |
string | No | URL of the source (http/https). Use an empty string to clear. |
intervalMinutes |
number | No | Run interval in minutes. Minimum: 60. Changing this does not recalculate nextRunAt. To also update the next run time, include nextRunAt in the same request. |
timezone |
string | No | Timezone (e.g., Asia/Tokyo) |
researchMode |
string | No | "normal" or "extended". Extended mode expands search and page traversal. Requires Pro plan or above. |
researchModelProfile |
string | No | "standard" or "precision". precision prioritizes better factual accuracy and higher report quality, and adds +4 credits on top of the research depth cost. Requires Pro plan or above. |
notificationConfigId |
string | null | No | ID of the notification config (Starter plan or above). Use null to detach. |
nextRunAt |
string | No | Next scheduled run time (ISO8601). No change if not specified. |
Response (200 OK):
| Name | Type | Description |
|---|---|---|
ok |
boolean | true |
data |
object | The updated Job Object |
POST /jobs/:id/status
Set a job's execution status to active or paused.
Body (JSON):
| Field | Type | Required | Description |
|---|---|---|---|
status |
string | Yes | "active" or "paused" |
Response (200 OK):
| Name | Type | Description |
|---|---|---|
ok |
boolean | true |
data |
object | The updated Job Object |
DELETE /jobs/:id
Deactivates a job. The job is set to paused and its next run schedule is cleared. This is a soft delete — the job record is not permanently removed.
Response (200 OK):
| Name | Type | Description |
|---|---|---|
ok |
boolean | true |
data |
object | The deactivated Job Object |
POST /jobs/:id/run
Trigger a job to run immediately (manual trigger). The run is enqueued and processed asynchronously.
Body (JSON):
| Field | Type | Required | Description |
|---|---|---|---|
tbs |
string | No | Search time range filter (internal use). Omit in most cases. |
Response (202 Accepted):
| Name | Type | Description |
|---|---|---|
ok |
boolean | true |
data.runId |
string | The ID assigned to this run |
data.started |
boolean | true indicates successful enqueue |
Error (409 Conflict):
{ "ok": false, "message": "Job is already running" }
Poll GET /jobs/:jobid/reports or GET /runs?jobId=:id to check the result.
GET /jobs/:id/running
Retrieve whether the job is currently running.
Response (200 OK):
| Name | Type | Description |
|---|---|---|
ok |
boolean | true |
data.jobId |
string | Target job ID |
data.running |
boolean | true when the job is currently executing |
data.runningStartedAt |
string | null | Execution start time (ISO8601) |
data.lockAgeMs |
number | null | Elapsed milliseconds since execution started |
Reports
Intelligence reports generated by your jobs.
GET /jobs/:jobid/reports
Fetch reports generated by a specific job, newest first. Returns 10 items per page (default) with cursor-based pagination.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
cursor |
string | Document ID of the last item on the previous page (value of pagination.nextCursor). Omit to start from the beginning. |
limit |
number | Items per page. Default: 10, Max: 100 |
Response (200 OK):
| Name | Type | Description |
|---|---|---|
ok |
boolean | true |
data |
array | An array of Report Objects |
pagination.nextCursor |
string | null | Cursor for the next page. null means this is the last page. |
pagination.hasMore |
boolean | Whether more items are available |
Example:
{
"ok": true,
"data": [
{
"id": "rpt_xyz001",
"jobId": "job_abc123",
"status": "success",
"subject": "Report 1",
"summary": "...",
"abstract": null,
"createdAt": "2026-03-29T12:00:00.000Z"
}
/* … */
],
"pagination": {
"nextCursor": "gU3Ed...",
"hasMore": true
}
}
GET /jobs/:jobid/reports/:reportid
Fetch detailed information for a single, specific report.
Response (200 OK):
| Name | Type | Description |
|---|---|---|
ok |
boolean | true |
data |
object | A Report Object |
GET /runs
Fetch the run log. Returns up to 1200 runs, newest first.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
jobId |
string | Filter by a specific job ID |
Response (200 OK):
| Name | Type | Description |
|---|---|---|
ok |
boolean | true |
data |
array | An array of Run Objects |
Notifications
GET /notifications
Fetch notification history. Returns up to 50 notifications, newest first.
Response (200 OK):
| Name | Type | Description |
|---|---|---|
ok |
boolean | true |
data |
array | An array of notification objects |
POST /notifications/read-all
Mark all unread notifications as read.
Response (200 OK):
{ "ok": true, "data": { "success": true } }
Trend Analysis
Fetch and generate AI-powered timeline analysis reports for a specific job.
Permissions: Starter plan or above
GET /jobs/:jobid/timeline-report
Fetch the latest trend analysis report for a specific job. Returns data: null if no report has been generated yet.
Response (200 OK):
| Name | Type | Description |
|---|---|---|
ok |
boolean | true |
data |
object | null | A Timeline Report Object, or null if not yet generated |
POST /jobs/:jobid/timeline-report/generate
Asynchronously trigger the generation of a new trend analysis report. At least 2 successful reports must exist for the target job.
Response (202 Accepted):
{ "ok": true, "data": { "generating": true } }
Poll GET /jobs/:jobid/timeline-report to check completion. The report is ready when data.status transitions from "generating" to a completed state.
Notification Configs
Manage external integrations for Slack, Microsoft Teams, a generic webhook, or email. Requires Starter plan or above.
GET /notification-configs
List all your notification configurations.
Response (200 OK):
| Name | Type | Description |
|---|---|---|
ok |
boolean | true |
data |
array | An array of Notification Config Objects |
POST /notification-configs
Create a new notification destination.
Body (JSON):
| Field | Type | Required | Description |
|---|---|---|---|
provider |
string | Yes | "slack", "teams", "webhook", or "email" |
name |
string | Yes | Display name for the config |
webhookUrl |
string | Required when provider is "slack", "teams", or "webhook" |
Webhook URL (https) |
email |
string | Required when provider is "email" |
Destination email address |
isActive |
boolean | No | Whether the config is active. Default: true |
Response (200 OK):
| Name | Type | Description |
|---|---|---|
ok |
boolean | true |
data |
object | The newly created Notification Config Object |
PATCH /notification-configs/:id
Update an existing notification configuration. Only the fields you provide will be updated.
Body (JSON):
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | No | Display name |
webhookUrl |
string | No | Webhook URL (https) |
email |
string | No | Email destination (only for provider: "email") |
isActive |
boolean | No | Whether the config is active |
Response (200 OK):
| Name | Type | Description |
|---|---|---|
ok |
boolean | true |
data |
object | The updated Notification Config Object |
DELETE /notification-configs/:id
Delete a notification configuration. Any jobs referencing this config will be automatically unlinked.
Response (200 OK):
{ "ok": true, "data": { "deleted": true } }
POST /notification-configs/:id/test
Send a test message to the notification destination. Returns 400 if the config is set to isActive: false.
Response (200 OK):
{ "ok": true, "data": { "sent": true } }
API Keys
API key management operations can only be performed using a user session (dashboard login). You cannot use an API key to manage other API keys. Requires Pro plan or above.
GET /api-keys
List your registered API keys. Returns up to 50.
Response (200 OK):
| Name | Type | Description |
|---|---|---|
ok |
boolean | true |
data |
array | An array of API key objects |
POST /api-keys
Create a new API key. The secret is only included in this response and cannot be retrieved again.
Body (JSON):
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Identifier for the key |
expiration |
string | No | Expiration date (ISO8601). No expiration if omitted. |
Response (200 OK):
| Name | Type | Description |
|---|---|---|
ok |
boolean | true |
data.apiKey |
object | Metadata of the created API key |
data.secret |
string | One-time secret (e.g., msk_xxxx). Store it securely — it will not be shown again. |
POST /api-keys/:id/disable
Disable an API key. A disabled key is immediately rejected for authentication, but can be enabled again later.
Response (200 OK):
{ "ok": true, "data": { "disabled": true, "apiKey": { "id": "...", "disabledAt": "2026-03-02T10:00:00.000Z" } } }
POST /api-keys/:id/enable
Re-enable a previously disabled API key. If the key is already expired, enabling it does not extend the expiration.
Response (200 OK):
{ "ok": true, "data": { "enabled": true, "apiKey": { "id": "...", "disabledAt": null } } }
DELETE /api-keys/:id
Physically delete an API key. This cannot be undone.
Response (200 OK):
{ "ok": true, "data": { "deleted": true } }
Account
GET /account/usage
Retrieve your current account usage (monthly report count, plan tier, etc.).
Response (200 OK):
{
"ok": true,
"data": {
"monthlyReportsGenerated": 42,
"tier": "starter",
"usageStartAt": "2026-03-01T00:00:00Z",
"usageEndAt": "2026-03-31T23:59:59Z"
}
}
Data Models
Job Object
{
"id": "job_abc123",
"name": "Competitor Analysis",
"sourceUrl": "https://example.com/news",
"promptInstruction": "Analyze major competitor developments",
"intervalMinutes": 1440,
"timezone": "Asia/Tokyo",
"status": "active",
"running": false,
"researchMode": "normal",
"researchModelProfile": "standard",
"notificationConfigId": "cfg_xyz456",
"lastRunAt": "2026-03-02T10:00:00.000Z",
"nextRunAt": "2026-03-03T10:00:00.000Z"
}
| Field | Type | Description |
|---|---|---|
id |
string | Unique job ID |
name |
string | Job identifier |
sourceUrl |
string | Source URL (empty string if not set) |
promptInstruction |
string | AI instructions |
intervalMinutes |
number | Run interval in minutes |
timezone |
string | Timezone (default UTC) |
status |
string | "active" or "paused" |
running |
boolean | Whether the job is currently executing |
researchMode |
string | "normal" or "extended". "extended" deepens search and page traversal |
researchModelProfile |
string | "standard" or "precision". precision prioritizes better factual accuracy and higher report quality |
notificationConfigId |
string | null | Linked notification config ID |
lastRunAt |
string | null | Last run time (ISO8601) |
nextRunAt |
string | null | Next scheduled run time (ISO8601) |
Report Object
{
"id": "rpt_def456",
"jobId": "job_abc123",
"status": "success",
"subject": "Competitor Analysis Report 2026-03-02",
"summary": "Key highlights this week include...",
"abstract": "Brief summary text (if available)",
"sources": [
{
"index": 1,
"url": "https://example.com/source"
}
],
"createdAt": "2026-03-02T10:05:00.000Z"
}
| Field | Type | Description |
|---|---|---|
id |
string | Unique report ID |
jobId |
string | Source job ID |
status |
string | "success", "failed", etc. |
subject |
string | Report title |
summary |
string | Full report content |
abstract |
string | null | Short summary (if available) |
sources |
array | Array of cited source records |
createdAt |
string | Generation time (ISO8601) |
Run Object
{
"id": "job_abc123-1709380000000",
"jobId": "job_abc123",
"status": "success",
"startedAt": "2026-03-02T10:00:00.000Z",
"finishedAt": "2026-03-02T10:04:58.000Z",
"durationMs": 298000,
"triggeredBy": "manual",
"errorCategory": null,
"errorMessage": null
}
| Field | Type | Description |
|---|---|---|
id |
string | Unique run ID |
jobId |
string | Source job ID |
status |
string | "success", "failed", "running", etc. |
startedAt |
string | null | Run start time (ISO8601) |
finishedAt |
string | null | Run end time (ISO8601) |
durationMs |
number | null | Duration in milliseconds |
triggeredBy |
string | "manual" or "schedule" |
errorCategory |
string | null | Error category (on failure) |
errorMessage |
string | null | Error message (on failure) |
Notification Config Object
{
"id": "cfg_xyz456",
"provider": "slack",
"name": "Dev Team Alerts",
"webhookUrlMasked": "https://hooks.slack.com/...****",
"destinationMasked": "https://hooks.slack.com/...****",
"isActive": true,
"createdAt": "2026-02-01T00:00:00.000Z",
"updatedAt": "2026-02-15T12:30:00.000Z"
}
| Field | Type | Description |
|---|---|---|
id |
string | Unique config ID |
provider |
string | "slack", "teams", "webhook", or "email" |
name |
string | Display name |
webhookUrlMasked |
string | Masked webhook URL (end obscured for security). For email destinations this mirrors emailMasked. |
emailMasked |
string | Masked email address (when provider: "email") |
destinationMasked |
string | Preferred masked destination (email or webhook) |
isActive |
boolean | Whether the config is active |
createdAt |
string | Creation time (ISO8601) |
updatedAt |
string | Last update time (ISO8601) |
Timeline Report Object
{
"id": "job_abc123",
"jobId": "job_abc123",
"subject": "Competitor Trend Timeline",
"body": "Analysis of the past N reports shows...",
"status": "done",
"reportCount": 12,
"createdAt": "2026-03-01T08:00:00.000Z",
"updatedAt": "2026-03-02T09:00:00.000Z"
}
| Field | Type | Description |
|---|---|---|
id |
string | Document ID (same as the job ID) |
jobId |
string | Source job ID |
subject |
string | null | Report title |
body |
string | null | Report content |
status |
string | "generating" while in progress; a completion value when done |
reportCount |
number | Number of reports used for analysis |
createdAt |
string | null | First generation time (ISO8601) |
updatedAt |
string | null | Last update time (ISO8601) |