use std::time::Duration;
use base64::Engine;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HelloResult {
#[serde(default)]
pub timestamp: String,
#[serde(default)]
pub client_ip: String,
#[serde(default)]
pub hai_public_key_fingerprint: String,
#[serde(default)]
pub message: String,
#[serde(default)]
pub hai_signed_ack: String,
#[serde(default)]
pub hello_id: String,
#[serde(default)]
pub test_scenario: Option<Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CreateAgentOptions {
pub name: String,
pub password: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub algorithm: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub data_directory: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub key_directory: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub config_path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub agent_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub domain: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub default_storage: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CreateAgentResult {
#[serde(default)]
pub agent_id: String,
#[serde(default)]
pub name: String,
#[serde(default)]
pub public_key_path: String,
#[serde(default)]
pub config_path: String,
#[serde(default)]
pub version: String,
#[serde(default)]
pub algorithm: String,
#[serde(default)]
pub private_key_path: String,
#[serde(default)]
pub data_directory: String,
#[serde(default)]
pub key_directory: String,
#[serde(default)]
pub domain: String,
#[serde(default)]
pub dns_record: String,
}
#[derive(Debug, Clone, Default)]
pub struct RotateKeysOptions {
pub register_with_hai: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RotationResult {
pub jacs_id: String,
pub old_version: String,
pub new_version: String,
pub new_public_key_hash: String,
pub registered_with_hai: bool,
pub signed_agent_json: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateAgentResult {
pub jacs_id: String,
pub old_version: String,
pub new_version: String,
pub signed_agent_json: String,
#[serde(default)]
pub registered_with_hai: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MigrateAgentResult {
pub jacs_id: String,
pub old_version: String,
pub new_version: String,
pub patched_fields: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CheckUsernameResult {
#[serde(default)]
pub available: bool,
#[serde(default)]
pub username: String,
#[serde(default)]
pub reason: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct RegisterAgentOptions {
pub agent_json: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub public_key_pem: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub owner_email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub domain: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct RegistrationResult {
#[serde(default)]
pub success: bool,
#[serde(default)]
pub agent_id: String,
#[serde(default)]
pub jacs_id: String,
#[serde(default)]
pub dns_verified: bool,
#[serde(default)]
pub registrations: Vec<RegistrationEntry>,
#[serde(default)]
pub registered_at: String,
#[serde(default)]
pub message: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RegistrationEntry {
#[serde(default)]
pub key_id: String,
#[serde(default)]
pub algorithm: String,
#[serde(default)]
pub signature_json: String,
#[serde(default)]
pub signed_at: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerifyAgentResult {
#[serde(default)]
pub jacs_id: String,
#[serde(default)]
pub registered: bool,
#[serde(default)]
pub registrations: Vec<RegistrationEntry>,
#[serde(default)]
pub dns_verified: bool,
#[serde(default)]
pub registered_at: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JobResponseResult {
#[serde(default)]
pub success: bool,
#[serde(default)]
pub job_id: String,
#[serde(default)]
pub message: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClaimUsernameResult {
#[serde(default)]
pub username: String,
#[serde(default)]
pub email: String,
#[serde(default)]
pub agent_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct UpdateUsernameResult {
#[serde(default)]
pub username: String,
#[serde(default)]
pub email: String,
#[serde(default)]
pub previous_username: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DeleteUsernameResult {
#[serde(default)]
pub released_username: String,
#[serde(default)]
pub cooldown_until: String,
#[serde(default)]
pub message: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum FieldStatus {
Pass,
Modified,
Fail,
Unverifiable,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct FieldResult {
#[serde(default)]
pub field: String,
pub status: FieldStatus,
#[serde(skip_serializing_if = "Option::is_none")]
pub original_hash: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub current_hash: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub original_value: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub current_value: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ChainEntry {
#[serde(default)]
pub signer: String,
#[serde(default)]
pub jacs_id: String,
#[serde(default)]
pub valid: bool,
#[serde(default)]
pub forwarded: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailVerificationResultV2 {
pub valid: bool,
#[serde(default)]
pub jacs_id: String,
#[serde(default)]
pub algorithm: String,
#[serde(default)]
pub reputation_tier: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub dns_verified: Option<bool>,
#[serde(default)]
pub field_results: Vec<FieldResult>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub chain: Vec<ChainEntry>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub agent_status: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub benchmarks_completed: Vec<String>,
}
impl EmailVerificationResultV2 {
pub fn err(jacs_id: &str, reputation_tier: &str, error: &str) -> Self {
Self {
valid: false,
jacs_id: jacs_id.to_string(),
algorithm: String::new(),
reputation_tier: reputation_tier.to_string(),
dns_verified: None,
field_results: Vec::new(),
chain: Vec::new(),
error: Some(error.to_string()),
agent_status: None,
benchmarks_completed: Vec::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailAttachment {
pub filename: String,
pub content_type: String,
#[serde(skip)]
pub data: Vec<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub data_base64: Option<String>,
}
impl EmailAttachment {
pub fn new(filename: String, content_type: String, data: Vec<u8>) -> Self {
Self {
filename,
content_type,
data,
data_base64: None,
}
}
pub fn effective_data(&self) -> Vec<u8> {
if !self.data.is_empty() {
return self.data.clone();
}
if let Some(ref b64) = self.data_base64 {
if let Ok(decoded) = base64::engine::general_purpose::STANDARD.decode(b64) {
return decoded;
}
}
Vec::new()
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SendEmailOptions {
pub to: String,
pub subject: String,
pub body: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub cc: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub bcc: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub in_reply_to: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub attachments: Vec<EmailAttachment>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub labels: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub append_footer: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SendEmailResult {
#[serde(default)]
pub message_id: String,
#[serde(default)]
pub status: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ListMessagesOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub offset: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub direction: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_read: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub folder: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub has_attachments: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailMessage {
#[serde(default)]
pub id: String,
#[serde(default)]
pub direction: String,
#[serde(default)]
pub from_address: String,
#[serde(default)]
pub to_address: String,
#[serde(default)]
pub subject: String,
#[serde(default)]
pub body_text: String,
#[serde(default)]
pub message_id: Option<String>,
#[serde(default)]
pub in_reply_to: Option<String>,
#[serde(default)]
pub is_read: bool,
#[serde(default)]
pub delivery_status: String,
#[serde(default)]
pub created_at: String,
#[serde(default)]
pub read_at: Option<String>,
#[serde(default)]
pub jacs_verified: Option<bool>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub cc_addresses: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub labels: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trust_score: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub body_text_clean: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub quoted_text: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub thread: Option<Vec<EmailMessage>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SearchOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub q: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub direction: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub from_address: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub to_address: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub since: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub until: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_read: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub jacs_verified: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub folder: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub has_attachments: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub offset: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailStatus {
#[serde(default)]
pub email: String,
#[serde(default)]
pub status: String,
#[serde(default)]
pub tier: String,
#[serde(default)]
pub billing_tier: String,
#[serde(default)]
pub messages_sent_24h: i32,
#[serde(default)]
pub daily_limit: i32,
#[serde(default)]
pub daily_used: i32,
#[serde(default)]
pub resets_at: String,
#[serde(default)]
pub messages_sent_total: i32,
#[serde(default)]
pub external_enabled: bool,
#[serde(default)]
pub external_sends_today: i32,
#[serde(default)]
pub last_tier_change: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub volume: Option<EmailVolumeInfo>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub delivery: Option<EmailDeliveryInfo>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reputation: Option<EmailReputationInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EmailVolumeInfo {
#[serde(default)]
pub sent_total: i64,
#[serde(default)]
pub received_total: i64,
#[serde(default)]
pub sent_24h: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EmailDeliveryInfo {
#[serde(default)]
pub bounce_count: i32,
#[serde(default)]
pub spam_report_count: i32,
#[serde(default)]
pub delivery_rate: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EmailReputationInfo {
#[serde(default)]
pub score: f64,
#[serde(default)]
pub tier: String,
#[serde(default)]
pub email_score: f64,
#[serde(default)]
pub hai_score: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Contact {
#[serde(default)]
pub email: String,
#[serde(default)]
pub display_name: Option<String>,
#[serde(default)]
pub last_contact: String,
#[serde(default)]
pub jacs_verified: bool,
#[serde(default)]
pub reputation_tier: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeyRegistryResponse {
#[serde(default)]
pub email: String,
#[serde(default)]
pub jacs_id: String,
#[serde(default)]
pub public_key: String,
#[serde(default)]
pub algorithm: String,
#[serde(default)]
pub reputation_tier: String,
#[serde(default)]
pub registered_at: String,
#[serde(default)]
pub agent_status: Option<String>,
#[serde(default)]
pub benchmarks_completed: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PublicKeyInfo {
#[serde(default)]
pub jacs_id: String,
#[serde(default)]
pub version: String,
#[serde(default)]
pub public_key: String,
#[serde(default)]
pub public_key_raw_b64: String,
#[serde(default)]
pub algorithm: String,
#[serde(default)]
pub public_key_hash: String,
#[serde(default)]
pub status: String,
#[serde(default)]
pub dns_verified: bool,
#[serde(default)]
pub created_at: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentKeyHistory {
#[serde(default)]
pub jacs_id: String,
#[serde(default)]
pub keys: Vec<PublicKeyInfo>,
#[serde(default)]
pub total: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DocumentVerificationResult {
#[serde(default)]
pub valid: bool,
#[serde(default)]
pub verified_at: String,
#[serde(default)]
pub document_type: String,
#[serde(default)]
pub issuer_verified: bool,
#[serde(default)]
pub signature_verified: bool,
#[serde(default)]
pub signer_id: String,
#[serde(default)]
pub signed_at: String,
#[serde(default)]
pub error: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignedPayload {
pub signed_document: String,
pub agent_jacs_id: String,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
#[serde(rename_all = "lowercase")]
pub enum TransportType {
#[default]
Sse,
Ws,
}
impl TransportType {
pub fn as_str(self) -> &'static str {
match self {
Self::Sse => "sse",
Self::Ws => "ws",
}
}
}
#[derive(Debug, Clone)]
pub struct ProRunOptions {
pub transport: TransportType,
pub poll_interval: Duration,
pub poll_timeout: Duration,
}
impl Default for ProRunOptions {
fn default() -> Self {
Self {
transport: TransportType::Sse,
poll_interval: Duration::from_secs(2),
poll_timeout: Duration::from_secs(300),
}
}
}
pub type DnsCertifiedRunOptions = ProRunOptions;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TranscriptMessage {
#[serde(default)]
pub role: String,
#[serde(default)]
pub content: String,
#[serde(default)]
pub timestamp: String,
#[serde(default)]
pub annotations: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct FreeChaoticResult {
#[serde(default)]
pub success: bool,
#[serde(default)]
pub run_id: String,
#[serde(default)]
pub transcript: Vec<TranscriptMessage>,
#[serde(default)]
pub upsell_message: String,
#[serde(default)]
pub raw_response: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ProRunResult {
#[serde(default)]
pub success: bool,
#[serde(default)]
pub run_id: String,
#[serde(default)]
pub score: f64,
#[serde(default)]
pub transcript: Vec<TranscriptMessage>,
#[serde(default)]
pub payment_id: String,
#[serde(default)]
pub raw_response: Value,
}
pub type DnsCertifiedResult = ProRunResult;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignedDocument {
pub key: String,
pub json: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DocSearchResults {
pub results: Vec<DocSearchHit>,
pub total_count: usize,
pub method: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DocSearchHit {
pub key: String,
pub json: String,
pub score: f64,
pub matched_fields: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct StorageCapabilities {
pub fulltext: bool,
pub vector: bool,
pub query_by_field: bool,
pub query_by_type: bool,
pub pagination: bool,
pub tombstone: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DocVerificationResult {
pub key: String,
pub valid: bool,
pub error: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub signer_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timestamp: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub signer_name: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct VerificationStatus {
#[serde(default)]
pub jacs_valid: bool,
#[serde(default)]
pub dns_valid: bool,
#[serde(default)]
pub hai_registered: bool,
#[serde(default)]
pub badge: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AgentVerificationResult {
#[serde(default)]
pub agent_id: String,
#[serde(default)]
pub verification: VerificationStatus,
#[serde(default)]
pub hai_signatures: Vec<String>,
#[serde(default)]
pub verified_at: String,
#[serde(default)]
pub errors: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct VerifyAgentDocumentRequest {
pub agent_json: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub public_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub domain: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct HaiEvent {
#[serde(default)]
pub event_type: String,
#[serde(default)]
pub data: Value,
#[serde(default)]
pub id: Option<String>,
#[serde(default)]
pub raw: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailTemplate {
pub id: String,
pub agent_id: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub how_to_send: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub how_to_respond: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub goal: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rules: Option<String>,
pub created_at: String,
pub updated_at: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CreateEmailTemplateOptions {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub how_to_send: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub how_to_respond: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub goal: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rules: Option<String>,
}
fn serialize_double_option<S>(value: &Option<Option<String>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match value {
Some(inner) => inner.serialize(serializer),
None => serializer.serialize_none(),
}
}
fn deserialize_double_option<'de, D>(deserializer: D) -> Result<Option<Option<String>>, D::Error>
where
D: Deserializer<'de>,
{
let value: Option<String> = Option::deserialize(deserializer)?;
Ok(Some(value))
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct UpdateEmailTemplateOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
serialize_with = "serialize_double_option",
deserialize_with = "deserialize_double_option"
)]
pub how_to_send: Option<Option<String>>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
serialize_with = "serialize_double_option",
deserialize_with = "deserialize_double_option"
)]
pub how_to_respond: Option<Option<String>>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
serialize_with = "serialize_double_option",
deserialize_with = "deserialize_double_option"
)]
pub goal: Option<Option<String>>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
serialize_with = "serialize_double_option",
deserialize_with = "deserialize_double_option"
)]
pub rules: Option<Option<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ListEmailTemplatesOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub offset: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub q: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ListEmailTemplatesResult {
pub templates: Vec<EmailTemplate>,
pub total: i64,
pub limit: i64,
pub offset: i64,
}