Registro de actividad
Lee el registro de tu organización con las llamadas a la API y los eventos de archivos y registros.
Descripción general
El Registro de actividad es el registro propio de tu organización de lo que ocurrió en Inovacc: cada llamada a la API, y cada evento de archivo y de registro que esas llamadas causaron. Lo lees con un solo endpoint, GET /v1/activity en https://data.inovacc.dev, filtrado por tiempo, tipo de evento o hash de archivo.
El problema que resuelve es responder "quién hizo qué, y cuándo" sin construir tu propia pista de auditoría. Inovacc mantiene el registro de toda llamada a las API de Datos, de IA y de Eventos, sin importar si tu aplicación se acuerda de escribir algo. El registro es un hecho del servicio, no una opción, y una organización solo ve sus propios registros.
Úsalo para auditorías, para investigar una integración fallida ("¿qué llamadas se rechazaron esta mañana, y con qué código?"), para demostrar que un archivo fue entregado (el evento de descarga lleva el SHA-256 del archivo), y para una vista del tráfico por clave. Para ver cuánto costó tu uso de IA, usa el resumen de uso del Chat con IA (GET /v1/usage/summary): el Registro de actividad registra eventos, nunca precios.
Conceptos
Eventos y tipos. Cada registro es un evento con un kind:
| Tipo | Se escribe cuando |
|---|---|
api.call | cualquier llamada autenticada, incluidas las rechazadas (403, 429) |
record.write, record.delete | se crea, cambia o elimina un registro (uno por operación de un lote) |
blob.write, blob.read, blob.delete | se carga, descarga o elimina un blob de una base de datos |
file.upload.completed, file.download, file.delete | se escribe, descarga o elimina un archivo |
file.upload.started, file.processed | los escriben otros servicios de Inovacc, como las cargas y conversiones de IA |
Qué contiene un registro. La hora (at_ms, milisegundos Unix), la organización, quién actuó (actor_kind: user, key o service, y actor_id), el servicio de origen source, el method, la route (una plantilla como /v1/databases/{database}/collections/{collection}/records/{record_id}, nunca los ids en sí), el status, el outcome, el error_code de un fallo, la latencia, los tamaños de entrada y salida, y para un archivo su tamaño, tipo y SHA-256. related apunta a la base de datos, la colección y el registro, o a la carga o el trabajo, de que se trate. Todas las claves están siempre presentes, con null cuando no aplican.
Lo que nunca contiene. Un prompt, el cuerpo de una respuesta ni el contenido de un archivo. Las llamadas de IA nunca registran el nombre de un archivo.
País. Un registro api.call lleva country: el país de quien llama tal como lo ve el borde de red de Inovacc (dos letras, XX cuando se desconoce). Quien llama no puede establecerlo, y el registro no contiene ciudad, dirección IP ni coordenadas.
Retención. Los registros se conservan un año. Los últimos 30 días responden de inmediato; los registros más antiguos vienen del archivo y tardan más.
Cómo funciona
Llama a GET /v1/activity con Authorization: Bearer <credential>. La credencial necesita el permiso de lectura de actividad a nivel de organización; una credencial limitada a registros no lo tiene. La organización es siempre la de la credencial: no hay ningún parámetro que nombre otra.
Todos los parámetros son opcionales:
| Parámetro | Significado |
|---|---|
from, to | milisegundos Unix, UTC; from inclusivo, to exclusivo |
kind | uno o más tipos, separados por comas |
file_sha256 | 64 caracteres hexadecimales en minúscula: todos los eventos de ese archivo |
limit | de 1 a 500, por defecto 100 |
cursor | el next_cursor de la página anterior, sin cambios |
Cualquier otro parámetro, uno repetido o vacío, o un valor no válido es 400 invalid_query, y no se lee nada. La respuesta viene con lo más reciente primero: {"items":[...],"next_cursor":<string|null>,"archive_searched":<bool>}. Sigue next_cursor hasta que sea null. archive_searched te indica que la lectura llegó más allá de los últimos 30 días. Una lectura puede retroceder como máximo 366 días por llamada.
Los registros aparecen unos segundos después de la llamada, no al instante. El registro ocurre después de la respuesta y nunca la modifica: si no se puede escribir en el registro, la llamada se responde como de costumbre. Una descarga desde Datos o Archivos se registra cuando comienza; una descarga desde la API de IA se registra cuando la transferencia se completa. La API de IA también ofrece GET /v1/activity en https://ai.inovacc.dev para sus propias llamadas, con from y to como instantes RFC 3339; leerlo allí no se cobra.
Primeros pasos
Necesitas una credencial con el permiso de lectura de actividad (Autenticación).
- Haz una llamada para registrar. Escribe un registro en Datos o carga un archivo en Archivos.
- Lee los eventos más recientes.
GET /v1/activity?limit=10(consulta los ejemplos). Tu llamada está ahí comoapi.call, con su plantilla de ruta y su estado, seguida de surecord.writeofile.upload.completed. - Filtra por tipo.
GET /v1/activity?kind=api.call&limit=50muestra solo llamadas; mirastatusyerror_codepara encontrar rechazos. - Sigue un archivo. Toma el
file_sha256de tu carga y llama aGET /v1/activity?file_sha256=<hash>: se listan todas las cargas, descargas y eliminaciones de ese contenido. - Pagina. Pasa el
next_cursorde vuelta comocursorhasta que seanull.
Casos de uso
Una respuesta de auditoría. Un cliente pregunta quién eliminó un registro el martes pasado. Filtra kind=record.delete con from y to alrededor de ese día; el evento nombra al actor y related nombra la base de datos, la colección y el registro.
Prueba de entrega. Un equipo de cumplimiento debe demostrar que un informe llegó a un socio. El evento file.download lleva el SHA-256 y el tamaño de los bytes enviados, y la hora, que se pueden cotejar con el archivo original.
Salud de una integración. Una integración empieza a fallar por la noche. Filtrar kind=api.call para esa ventana muestra el status y el error_code de cada llamada hecha con la clave de la integración, por ejemplo una racha de 429 rate_limited que señala la falta de una espera creciente.
Límites y precios
| Límite | Valor |
|---|---|
| Retención | un año; los últimos 30 días responden de inmediato |
| Rango de una lectura | 366 días |
| Tamaño de página | 1 a 500 (por defecto 100) |
| Demora antes de que un evento sea legible | unos segundos |
| Tasa | 600 solicitudes por minuto por titular de credencial, por ubicación |
Precios: a consultar.
Errores
| Estado | Código | Qué significa y qué hacer |
|---|---|---|
| 400 | invalid_query | Un parámetro es desconocido, está repetido, vacío o no es válido; corrige la consulta. |
| 401 | invalid_credentials | Envía una credencial válida. |
| 403 | forbidden | La credencial no tiene el permiso de lectura de actividad. |
| 429 | rate_limited | Reduce el ritmo. |
| 503 | service_unavailable | No se puede leer el registro ahora; reintenta más tarde. |
Buenas prácticas
- Filtra en el servidor: pasa
kind,fromytoen lugar de leer todo y filtrar en tu código. - Conserva el
at_msmás reciente que procesaste y lee desde ahí en la siguiente ejecución, siguiendo los cursores hasta el final. - Usa
file_sha256para rastrear un archivo a través de cargas, descargas y procesamiento de IA. - Espera unos segundos de demora; no trates como un fallo la ausencia de un evento recién generado.
- Ignora las claves que no conozcas: los registros pueden ganar campos.
- Da a cada integración su propia clave, para que
actor_idte diga cuál actuó.
Relacionados
- Datos, Archivos, Eventos
- Chat con IA: uso y costo por clave y ruta
- Autenticación, Errores, Límites
Autenticación
Toda llamada lleva tu clave; tu organización viene de ella. Consulta la guía de autenticación.
AuthorizationEndpoints
GET /v1/activity
Ejemplo
Lenguaje
⋮
cURL
curl "https://data.inovacc.dev/v1/activity" \
-H "Authorization: Bearer $INOVACC_API_KEY"curl "https://data.inovacc.dev/v1/activity" \
-H "Authorization: Bearer $INOVACC_API_KEY"
TypeScript
const url = "https://data.inovacc.dev/v1/activity";
const response = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.INOVACC_API_KEY}`,
},
});
console.log(response.status, await response.text());const url = "https://data.inovacc.dev/v1/activity";
const response = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.INOVACC_API_KEY}`,
},
});
console.log(response.status, await response.text());
Python
import os
import urllib.request
request = urllib.request.Request(
"https://data.inovacc.dev/v1/activity",
method="GET",
headers={
"User-Agent": "inovacc-python-sample",
"Authorization": "Bearer " + os.environ["INOVACC_API_KEY"],
},
)
with urllib.request.urlopen(request) as response:
print(response.status, response.read().decode())import os
import urllib.request
request = urllib.request.Request(
"https://data.inovacc.dev/v1/activity",
method="GET",
headers={
"User-Agent": "inovacc-python-sample",
"Authorization": "Bearer " + os.environ["INOVACC_API_KEY"],
},
)
with urllib.request.urlopen(request) as response:
print(response.status, response.read().decode())
Go
package main
import (
"fmt"
"io"
"net/http"
"os"
)
const url = "https://data.inovacc.dev/v1/activity"
func main() {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INOVACC_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, err := io.ReadAll(res.Body)
if err != nil {
panic(err)
}
fmt.Println(res.Status, string(out))
}package main
import (
"fmt"
"io"
"net/http"
"os"
)
const url = "https://data.inovacc.dev/v1/activity"
func main() {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INOVACC_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, err := io.ReadAll(res.Body)
if err != nil {
panic(err)
}
fmt.Println(res.Status, string(out))
}
Rust
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }
fn main() -> Result<(), Box<dyn std::error::Error>> {
let api_key = std::env::var("INOVACC_API_KEY")?;
let response = reqwest::blocking::Client::new()
.get("https://data.inovacc.dev/v1/activity")
.bearer_auth(api_key)
.send()?;
println!("{} {}", response.status(), response.text()?);
Ok(())
}// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }
fn main() -> Result<(), Box<dyn std::error::Error>> {
let api_key = std::env::var("INOVACC_API_KEY")?;
let response = reqwest::blocking::Client::new()
.get("https://data.inovacc.dev/v1/activity")
.bearer_auth(api_key)
.send()?;
println!("{} {}", response.status(), response.text()?);
Ok(())
}
JavaScript
const url = "https://data.inovacc.dev/v1/activity";
const response = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.INOVACC_API_KEY}`,
},
});
console.log(response.status, await response.text());const url = "https://data.inovacc.dev/v1/activity";
const response = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.INOVACC_API_KEY}`,
},
});
console.log(response.status, await response.text());
PHP
<?php
$curl = curl_init('https://data.inovacc.dev/v1/activity');
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('INOVACC_API_KEY'),
],
]);
$response = curl_exec($curl);
echo curl_getinfo($curl, CURLINFO_HTTP_CODE), ' ', $response, "\n";
curl_close($curl);<?php
$curl = curl_init('https://data.inovacc.dev/v1/activity');
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('INOVACC_API_KEY'),
],
]);
$response = curl_exec($curl);
echo curl_getinfo($curl, CURLINFO_HTTP_CODE), ' ', $response, "\n";
curl_close($curl);
Ruby
require "net/http"
require "uri"
uri = URI('https://data.inovacc.dev/v1/activity')
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch('INOVACC_API_KEY')}"
response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
http.request(request)
end
puts "#{response.code} #{response.body}"require "net/http"
require "uri"
uri = URI('https://data.inovacc.dev/v1/activity')
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch('INOVACC_API_KEY')}"
response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
http.request(request)
end
puts "#{response.code} #{response.body}"
Java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Main {
public static void main(String[] args) throws Exception {
String apiKey = System.getenv("INOVACC_API_KEY");
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://data.inovacc.dev/v1/activity"))
.header("Authorization", "Bearer " + apiKey)
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode() + " " + response.body());
}
}import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Main {
public static void main(String[] args) throws Exception {
String apiKey = System.getenv("INOVACC_API_KEY");
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://data.inovacc.dev/v1/activity"))
.header("Authorization", "Bearer " + apiKey)
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode() + " " + response.body());
}
}
C#
var url = "https://data.inovacc.dev/v1/activity";
var apiKey = Environment.GetEnvironmentVariable("INOVACC_API_KEY");
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("Authorization", $"Bearer {apiKey}");
using var response = await client.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");var url = "https://data.inovacc.dev/v1/activity";
var apiKey = Environment.GetEnvironmentVariable("INOVACC_API_KEY");
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("Authorization", $"Bearer {apiKey}");
using var response = await client.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");
Kotlin
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
fun main() {
val apiKey = System.getenv("INOVACC_API_KEY")
val request = HttpRequest.newBuilder()
.uri(URI.create("https://data.inovacc.dev/v1/activity"))
.header("Authorization", "Bearer " + apiKey)
.GET()
.build()
val response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString())
println("${response.statusCode()} ${response.body()}")
}import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
fun main() {
val apiKey = System.getenv("INOVACC_API_KEY")
val request = HttpRequest.newBuilder()
.uri(URI.create("https://data.inovacc.dev/v1/activity"))
.header("Authorization", "Bearer " + apiKey)
.GET()
.build()
val response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString())
println("${response.statusCode()} ${response.body()}")
}
Swift
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
let apiKey = ProcessInfo.processInfo.environment["INOVACC_API_KEY"] ?? ""
let url = URL(string: "https://data.inovacc.dev/v1/activity")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
let (data, response) = try await URLSession.shared.data(for: request)
let status = (response as? HTTPURLResponse)?.statusCode ?? 0
print(status, String(decoding: data, as: UTF8.self))import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
let apiKey = ProcessInfo.processInfo.environment["INOVACC_API_KEY"] ?? ""
let url = URL(string: "https://data.inovacc.dev/v1/activity")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
let (data, response) = try await URLSession.shared.data(for: request)
let status = (response as? HTTPURLResponse)?.statusCode ?? 0
print(status, String(decoding: data, as: UTF8.self))
Detalles de la fuente
- Id
- identity/component-taxonomy/capabilities#activity
- Repositorio
- identity
- Ruta
- contracts/component-taxonomy/catalog/capabilities.json
- Commit
- 08ef3cd75d82
- Id
- identity/component-taxonomy/quickstart#activity
- Repositorio
- identity
- Ruta
- contracts/component-taxonomy/catalog/capabilities.json
- Commit
- 08ef3cd75d82