Merge branch 'develop' into fix-signup-captcha

This commit is contained in:
Sivin Varghese
2025-11-13 13:34:40 +05:30
committed by GitHub
36 changed files with 1495 additions and 14 deletions
@@ -22,9 +22,10 @@ class Api::V1::Accounts::ArticlesController < Api::V1::Accounts::BaseController
def edit; end
def create
@article = @portal.articles.create!(article_params)
params_with_defaults = article_params
params_with_defaults[:status] ||= :draft
@article = @portal.articles.create!(params_with_defaults)
@article.associate_root_article(article_params[:associated_article_id])
@article.draft!
render json: { error: @article.errors.messages }, status: :unprocessable_entity and return unless @article.valid?
end
@@ -23,7 +23,7 @@ class Api::V1::Accounts::WebhooksController < Api::V1::Accounts::BaseController
private
def webhook_params
params.require(:webhook).permit(:inbox_id, :url, subscriptions: [])
params.require(:webhook).permit(:inbox_id, :name, :url, subscriptions: [])
end
def fetch_webhook
@@ -46,6 +46,10 @@
"CONVERSATION_TYPING_OFF": "Conversation Typing Off"
}
},
"NAME": {
"LABEL": "Webhook Name",
"PLACEHOLDER": "Enter the name of the webhook"
},
"END_POINT": {
"LABEL": "Webhook URL",
"PLACEHOLDER": "Example: {webhookExampleURL}",
@@ -55,6 +55,7 @@ export default {
data() {
return {
url: this.value.url || '',
name: this.value.name || '',
subscriptions: this.value.subscriptions || [],
supportedWebhookEvents: SUPPORTED_WEBHOOK_EVENTS,
};
@@ -68,11 +69,15 @@ export default {
}
);
},
webhookNameInputPlaceholder() {
return this.$t('INTEGRATION_SETTINGS.WEBHOOK.FORM.NAME.PLACEHOLDER');
},
},
methods: {
onSubmit() {
this.$emit('submit', {
url: this.url,
name: this.name,
subscriptions: this.subscriptions,
});
},
@@ -97,6 +102,15 @@ export default {
{{ $t('INTEGRATION_SETTINGS.WEBHOOK.FORM.END_POINT.ERROR') }}
</span>
</label>
<label>
{{ $t('INTEGRATION_SETTINGS.WEBHOOK.FORM.NAME.LABEL') }}
<input
v-model="name"
type="text"
name="name"
:placeholder="webhookNameInputPlaceholder"
/>
</label>
<label :class="{ error: v$.url.$error }" class="mb-2">
{{ $t('INTEGRATION_SETTINGS.WEBHOOK.FORM.SUBSCRIPTIONS.LABEL') }}
</label>
@@ -37,8 +37,16 @@ const subscribedEvents = computed(() => {
<template>
<tr>
<td class="py-4 ltr:pr-4 rtl:pl-4">
<div class="font-medium break-words text-n-slate-12">
{{ webhook.url }}
<div class="flex gap-2 font-medium break-words text-n-slate-12">
<template v-if="webhook.name">
{{ webhook.name }}
<span class="text-n-slate-11">
{{ webhook.url }}
</span>
</template>
<template v-else>
{{ webhook.url }}
</template>
</div>
<div class="block mt-1 text-sm text-n-slate-11">
<span class="font-medium">
+17
View File
@@ -35,4 +35,21 @@ class ReportingEvent < ApplicationRecord
belongs_to :user, optional: true
belongs_to :inbox, optional: true
belongs_to :conversation, optional: true
# Scopes for filtering
scope :filter_by_date_range, lambda { |range|
where(created_at: range) if range.present?
}
scope :filter_by_inbox_id, lambda { |inbox_id|
where(inbox_id: inbox_id) if inbox_id.present?
}
scope :filter_by_user_id, lambda { |user_id|
where(user_id: user_id) if user_id.present?
}
scope :filter_by_name, lambda { |name|
where(name: name) if name.present?
}
end
+1
View File
@@ -3,6 +3,7 @@
# Table name: webhooks
#
# id :bigint not null, primary key
# name :string
# subscriptions :jsonb
# url :string
# webhook_type :integer default("account_type")
@@ -1,4 +1,5 @@
json.id webhook.id
json.name webhook.name
json.url webhook.url
json.account_id webhook.account_id
json.subscriptions webhook.subscriptions
+2
View File
@@ -141,6 +141,7 @@ Rails.application.routes.draw do
post :custom_attributes
get :attachments
get :inbox_assistant
get :reporting_events if ChatwootApp.enterprise?
end
end
@@ -186,6 +187,7 @@ Rails.application.routes.draw do
get :download
end
end
resources :reporting_events, only: [:index] if ChatwootApp.enterprise?
resources :custom_attribute_definitions, only: [:index, :show, :create, :update, :destroy]
resources :custom_filters, only: [:index, :show, :create, :update, :destroy]
resources :inboxes, only: [:index, :show, :create, :update, :destroy] do
@@ -0,0 +1,5 @@
class AddNameToWebhooks < ActiveRecord::Migration[7.1]
def change
add_column :webhooks, :name, :string, null: true
end
end
+1
View File
@@ -1230,6 +1230,7 @@ ActiveRecord::Schema[7.1].define(version: 2025_10_22_152158) do
t.datetime "updated_at", null: false
t.integer "webhook_type", default: 0
t.jsonb "subscriptions", default: ["conversation_status_changed", "conversation_updated", "conversation_created", "contact_created", "contact_updated", "message_created", "message_updated", "webwidget_triggered"]
t.string "name"
t.index ["account_id", "url"], name: "index_webhooks_on_account_id_and_url", unique: true
end
@@ -0,0 +1,30 @@
class Api::V1::Accounts::ReportingEventsController < Api::V1::Accounts::EnterpriseAccountsController
include DateRangeHelper
RESULTS_PER_PAGE = 25
before_action :check_admin_authorization?
before_action :set_reporting_events, only: [:index]
before_action :set_current_page, only: [:index]
def index
@reporting_events = @reporting_events.page(@current_page).per(RESULTS_PER_PAGE)
@total_count = @reporting_events.total_count
end
private
def set_reporting_events
@reporting_events = Current.account.reporting_events
.includes(:conversation, :user, :inbox)
.filter_by_date_range(range)
.filter_by_inbox_id(params[:inbox_id])
.filter_by_user_id(params[:user_id])
.filter_by_name(params[:name])
.order(created_at: :desc)
end
def set_current_page
@current_page = (params[:page] || 1).to_i
end
end
@@ -11,6 +11,10 @@ module Enterprise::Api::V1::Accounts::ConversationsController
end
end
def reporting_events
@reporting_events = @conversation.reporting_events.order(created_at: :asc)
end
def permitted_update_params
super.merge(params.permit(:sla_policy_id))
end
@@ -0,0 +1,3 @@
json.array! @reporting_events do |reporting_event|
json.partial! 'api/v1/models/reporting_event', formats: [:json], reporting_event: reporting_event
end
@@ -0,0 +1,11 @@
json.payload do
json.array! @reporting_events do |reporting_event|
json.partial! 'api/v1/models/reporting_event', formats: [:json], reporting_event: reporting_event
end
end
json.meta do
json.count @total_count
json.current_page @current_page
json.total_pages @reporting_events.total_pages
end
@@ -0,0 +1,12 @@
json.id reporting_event.id
json.name reporting_event.name
json.value reporting_event.value
json.value_in_business_hours reporting_event.value_in_business_hours
json.event_start_time reporting_event.event_start_time
json.event_end_time reporting_event.event_end_time
json.account_id reporting_event.account_id
json.inbox_id reporting_event.inbox_id
json.user_id reporting_event.user_id
json.conversation_id reporting_event.conversation_id
json.created_at reporting_event.created_at
json.updated_at reporting_event.updated_at
@@ -36,7 +36,7 @@ RSpec.describe 'Api::V1::Accounts::Articles', type: :request do
expect(response).to have_http_status(:success)
json_response = response.parsed_body
expect(json_response['payload']['title']).to eql('MyTitle')
expect(json_response['payload']['status']).to eql('draft')
expect(json_response['payload']['status']).to eql('published')
expect(json_response['payload']['position']).to be(3)
end
@@ -59,10 +59,30 @@ RSpec.describe 'Api::V1::Accounts::Articles', type: :request do
expect(response).to have_http_status(:success)
json_response = response.parsed_body
expect(json_response['payload']['title']).to eql('MyTitle')
expect(json_response['payload']['status']).to eql('draft')
expect(json_response['payload']['status']).to eql('published')
expect(json_response['payload']['position']).to be(3)
end
it 'creates article as draft when status is not provided' do
article_params = {
article: {
category_id: category.id,
description: 'test description',
title: 'DraftTitle',
slug: 'draft-title',
content: 'This is my draft content.',
author_id: agent.id
}
}
post "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/articles",
params: article_params,
headers: admin.create_new_auth_token
expect(response).to have_http_status(:success)
json_response = response.parsed_body
expect(json_response['payload']['title']).to eql('DraftTitle')
expect(json_response['payload']['status']).to eql('draft')
end
it 'associate to the root article' do
root_article = create(:article, category: category, slug: 'root-article', portal: portal, account_id: account.id, author_id: agent.id,
associated_article_id: nil)
@@ -3,7 +3,7 @@ require 'rails_helper'
RSpec.describe 'Webhooks API', type: :request do
let(:account) { create(:account) }
let(:inbox) { create(:inbox, account: account) }
let(:webhook) { create(:webhook, account: account, inbox: inbox, url: 'https://hello.com') }
let(:webhook) { create(:webhook, account: account, inbox: inbox, url: 'https://hello.com', name: 'My Webhook') }
let(:administrator) { create(:user, account: account, role: :administrator) }
let(:agent) { create(:user, account: account, role: :agent) }
@@ -49,6 +49,16 @@ RSpec.describe 'Webhooks API', type: :request do
expect(response.parsed_body['payload']['webhook']['url']).to eql 'https://hello.com'
end
it 'creates webhook with name' do
post "/api/v1/accounts/#{account.id}/webhooks",
params: { account_id: account.id, inbox_id: inbox.id, url: 'https://hello.com', name: 'My Webhook' },
headers: administrator.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
expect(response.parsed_body['payload']['webhook']['name']).to eql 'My Webhook'
end
it 'throws error when invalid url provided' do
post "/api/v1/accounts/#{account.id}/webhooks",
params: { account_id: account.id, inbox_id: inbox.id, url: 'javascript:alert(1)' },
@@ -103,11 +113,12 @@ RSpec.describe 'Webhooks API', type: :request do
context 'when it is an authenticated admin user' do
it 'updates webhook' do
put "/api/v1/accounts/#{account.id}/webhooks/#{webhook.id}",
params: { url: 'https://hello.com' },
params: { url: 'https://hello.com', name: 'Another Webhook' },
headers: administrator.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
expect(response.parsed_body['payload']['webhook']['url']).to eql 'https://hello.com'
expect(response.parsed_body['payload']['webhook']['name']).to eql 'Another Webhook'
end
end
end
@@ -102,4 +102,146 @@ RSpec.describe 'Conversations API', type: :request do
end
end
end
describe 'GET /api/v1/accounts/{account.id}/conversations/:id/reporting_events' do
let(:conversation) { create(:conversation, account: account) }
let(:inbox) { conversation.inbox }
let(:agent) { administrator }
before do
# Create reporting events for this conversation
@event1 = create(:reporting_event,
account: account,
conversation: conversation,
inbox: inbox,
user: agent,
name: 'first_response',
value: 120,
created_at: 3.hours.ago)
@event2 = create(:reporting_event,
account: account,
conversation: conversation,
inbox: inbox,
user: agent,
name: 'reply_time',
value: 45,
created_at: 2.hours.ago)
@event3 = create(:reporting_event,
account: account,
conversation: conversation,
inbox: inbox,
user: agent,
name: 'resolution',
value: 300,
created_at: 1.hour.ago)
# Create an event for a different conversation (should not be included)
other_conversation = create(:conversation, account: account)
create(:reporting_event,
account: account,
conversation: other_conversation,
inbox: other_conversation.inbox,
user: agent,
name: 'other_conversation_event',
value: 60)
end
context 'when it is an unauthenticated user' do
it 'returns unauthorized' do
get "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/reporting_events",
as: :json
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an authenticated user with conversation access' do
it 'returns all reporting events for the conversation' do
get "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/reporting_events",
headers: administrator.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
json_response = response.parsed_body
# Should return array directly (no pagination)
expect(json_response).to be_an(Array)
expect(json_response.size).to eq(3)
# Check they are sorted by created_at asc (oldest first)
expect(json_response.first['name']).to eq('first_response')
expect(json_response.last['name']).to eq('resolution')
# Verify it doesn't include events from other conversations
event_names = json_response.map { |e| e['name'] }
expect(event_names).not_to include('other_conversation_event')
end
it 'returns empty array when conversation has no reporting events' do
conversation_without_events = create(:conversation, account: account)
get "/api/v1/accounts/#{account.id}/conversations/#{conversation_without_events.display_id}/reporting_events",
headers: administrator.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
json_response = response.parsed_body
expect(json_response).to be_an(Array)
expect(json_response).to be_empty
end
end
context 'when agent has limited access' do
let(:limited_agent) { create(:user, account: account, role: :agent) }
it 'returns unauthorized for unassigned conversation without permission' do
get "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/reporting_events",
headers: limited_agent.create_new_auth_token,
as: :json
expect(response).to have_http_status(:unauthorized)
end
it 'returns reporting events when agent is assigned to the conversation' do
conversation.update!(assignee: limited_agent)
# Also create inbox member for the agent
create(:inbox_member, user: limited_agent, inbox: conversation.inbox)
get "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/reporting_events",
headers: limited_agent.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
json_response = response.parsed_body
expect(json_response).to be_an(Array)
expect(json_response.size).to eq(3)
end
end
context 'when agent has team access' do
let(:team_agent) { create(:user, account: account, role: :agent) }
let(:team) { create(:team, account: account) }
before do
create(:team_member, team: team, user: team_agent)
conversation.update!(team: team)
end
it 'allows accessing conversation reporting events via team membership' do
get "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/reporting_events",
headers: team_agent.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
json_response = response.parsed_body
expect(json_response).to be_an(Array)
expect(json_response.size).to eq(3)
end
end
end
end
@@ -0,0 +1,217 @@
require 'rails_helper'
RSpec.describe 'Enterprise Reporting Events API', type: :request do
let!(:account) { create(:account) }
let!(:admin) { create(:user, account: account, role: :administrator) }
let!(:agent) { create(:user, account: account, role: :agent) }
let!(:inbox) { create(:inbox, account: account) }
let!(:conversation) { create(:conversation, account: account, inbox: inbox, assignee: agent) }
describe 'GET /api/v1/accounts/{account.id}/reporting_events' do
context 'when it is an unauthenticated user' do
it 'returns unauthorized' do
get "/api/v1/accounts/#{account.id}/reporting_events",
as: :json
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an authenticated normal agent user' do
it 'returns unauthorized' do
get "/api/v1/accounts/#{account.id}/reporting_events",
headers: agent.create_new_auth_token,
as: :json
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an authenticated admin user' do
before do
create(:reporting_event,
account: account,
conversation: conversation,
inbox: inbox,
user: agent,
name: 'first_response',
value: 120,
created_at: 3.days.ago)
create(:reporting_event,
account: account,
conversation: conversation,
inbox: inbox,
user: agent,
name: 'resolution',
value: 300,
created_at: 2.days.ago)
create(:reporting_event,
account: account,
conversation: conversation,
inbox: inbox,
user: agent,
name: 'reply_time',
value: 45,
created_at: 1.day.ago)
end
it 'fetches reporting events with pagination' do
get "/api/v1/accounts/#{account.id}/reporting_events",
headers: admin.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
json_response = response.parsed_body
# Check structure and pagination
expect(json_response).to have_key('payload')
expect(json_response).to have_key('meta')
expect(json_response['meta']['count']).to eq(3)
# Check events are sorted by created_at desc (newest first)
events = json_response['payload']
expect(events.size).to eq(3)
expect(events.first['name']).to eq('reply_time')
expect(events.last['name']).to eq('first_response')
end
it 'filters reporting events by date range using since and until' do
get "/api/v1/accounts/#{account.id}/reporting_events",
params: { since: 2.5.days.ago.to_time.to_i.to_s, until: 1.5.days.ago.to_time.to_i.to_s },
headers: admin.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
json_response = response.parsed_body
expect(json_response['meta']['count']).to eq(1)
expect(json_response['payload'].first['name']).to eq('resolution')
end
it 'filters reporting events by inbox_id' do
other_inbox = create(:inbox, account: account)
other_conversation = create(:conversation, account: account, inbox: other_inbox)
create(:reporting_event,
account: account,
conversation: other_conversation,
inbox: other_inbox,
user: agent,
name: 'other_inbox_event')
get "/api/v1/accounts/#{account.id}/reporting_events",
params: { inbox_id: inbox.id },
headers: admin.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
json_response = response.parsed_body
expect(json_response['meta']['count']).to eq(3)
expect(json_response['payload'].map { |e| e['name'] }).not_to include('other_inbox_event')
end
it 'filters reporting events by user_id (agent)' do
other_agent = create(:user, account: account, role: :agent)
create(:reporting_event,
account: account,
conversation: conversation,
inbox: inbox,
user: other_agent,
name: 'other_agent_event')
get "/api/v1/accounts/#{account.id}/reporting_events",
params: { user_id: agent.id },
headers: admin.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
json_response = response.parsed_body
expect(json_response['meta']['count']).to eq(3)
expect(json_response['payload'].map { |e| e['name'] }).not_to include('other_agent_event')
end
it 'filters reporting events by name' do
get "/api/v1/accounts/#{account.id}/reporting_events",
params: { name: 'first_response' },
headers: admin.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
json_response = response.parsed_body
expect(json_response['meta']['count']).to eq(1)
expect(json_response['payload'].first['name']).to eq('first_response')
end
it 'supports combining multiple filters' do
# Create more test data
other_conversation = create(:conversation, account: account, inbox: inbox, assignee: agent)
create(:reporting_event,
account: account,
conversation: other_conversation,
inbox: inbox,
user: agent,
name: 'first_response',
value: 90,
created_at: 2.days.ago)
get "/api/v1/accounts/#{account.id}/reporting_events",
params: {
inbox_id: inbox.id,
user_id: agent.id,
name: 'first_response',
since: 4.days.ago.to_time.to_i.to_s,
until: Time.zone.now.to_time.to_i.to_s
},
headers: admin.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
json_response = response.parsed_body
expect(json_response['meta']['count']).to eq(2)
expect(json_response['payload'].map { |e| e['name'] }).to all(eq('first_response'))
end
context 'with pagination' do
before do
# Create more events to test pagination
30.times do |i|
create(:reporting_event,
account: account,
conversation: conversation,
inbox: inbox,
user: agent,
name: "event_#{i}",
created_at: i.hours.ago)
end
end
it 'returns 25 events per page by default' do
get "/api/v1/accounts/#{account.id}/reporting_events",
headers: admin.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
json_response = response.parsed_body
expect(json_response['payload'].size).to eq(25)
expect(json_response['meta']['count']).to eq(33) # 30 + 3 original events
expect(json_response['meta']['current_page']).to eq(1)
end
it 'supports page navigation' do
get "/api/v1/accounts/#{account.id}/reporting_events",
params: { page: 2 },
headers: admin.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
json_response = response.parsed_body
expect(json_response['payload'].size).to eq(8) # Remaining events
expect(json_response['meta']['current_page']).to eq(2)
end
end
end
end
end
+7 -5
View File
@@ -1,11 +1,13 @@
FactoryBot.define do
factory :reporting_event do
name { 'MyString' }
name { 'first_response' }
value { 1.5 }
value_in_business_hours { 1 }
account_id { 1 }
inbox_id { 1 }
user_id { 1 }
conversation_id { 1 }
account
inbox { association :inbox, account: account }
user { association :user, account: account }
conversation { association :conversation, account: account, inbox: inbox }
event_start_time { 2.hours.ago }
event_end_time { 1.hour.ago }
end
end
+1
View File
@@ -3,6 +3,7 @@ FactoryBot.define do
account_id { 1 }
inbox_id { 1 }
url { 'https://api.chatwoot.com' }
name { 'My Webhook' }
subscriptions do
%w[
conversation_status_changed
+6
View File
@@ -248,3 +248,9 @@ contact_conversations_response:
$ref: ./resource/contact_conversations_response.yml
contactable_inboxes_response:
$ref: ./resource/contactable_inboxes_response.yml
reporting_event:
$ref: ./resource/reporting_event.yml
reporting_event_meta:
$ref: ./resource/reporting_event_meta.yml
reporting_events_list_response:
$ref: ./resource/reporting_events_list_response.yml
@@ -4,6 +4,9 @@ properties:
type: string
description: The url where the events should be sent
example: https://example.com/webhook
name:
type: string
description: The name of the webhook
subscriptions:
type: array
items:
@@ -0,0 +1,47 @@
type: object
properties:
id:
type: number
description: ID of the reporting event
name:
type: string
description: Name of the event (e.g., first_response, resolution, reply_time)
value:
type: number
format: double
description: Value of the metric in seconds
value_in_business_hours:
type: number
format: double
description: Value of the metric in seconds, calculated only for business hours
event_start_time:
type: string
format: date-time
description: The timestamp when the event started
event_end_time:
type: string
format: date-time
description: The timestamp when the event ended
account_id:
type: number
description: ID of the account
conversation_id:
type: number
nullable: true
description: ID of the conversation
inbox_id:
type: number
nullable: true
description: ID of the inbox
user_id:
type: number
nullable: true
description: ID of the user/agent
created_at:
type: string
format: date-time
description: The timestamp when the reporting event was created
updated_at:
type: string
format: date-time
description: The timestamp when the reporting event was last updated
@@ -0,0 +1,11 @@
type: object
properties:
count:
type: integer
description: Total number of reporting events
current_page:
type: integer
description: Current page number
total_pages:
type: integer
description: Total number of pages
@@ -0,0 +1,10 @@
type: object
properties:
meta:
$ref: '#/components/schemas/reporting_event_meta'
description: Metadata about the reporting events list response
payload:
type: array
items:
$ref: '#/components/schemas/reporting_event'
description: List of reporting events
+3
View File
@@ -6,6 +6,9 @@ properties:
url:
type: string
description: The url to which the events will be send
name:
type: string
description: The name of the webhook
subscriptions:
type: array
items:
@@ -0,0 +1,29 @@
tags:
- Conversations
operationId: get-conversation-reporting-events
summary: Conversation Reporting Events
security:
- userApiKey: []
description: Get reporting events for a specific conversation. This endpoint returns events such as first response time, resolution time, and other metrics for the conversation, sorted by creation time in ascending order.
responses:
'200':
description: Success
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/reporting_event'
description: Array of reporting events for the conversation
'403':
description: Access denied
content:
application/json:
schema:
$ref: '#/components/schemas/bad_request_error'
'404':
description: Conversation not found
content:
application/json:
schema:
$ref: '#/components/schemas/bad_request_error'
@@ -0,0 +1,47 @@
tags:
- Reports
operationId: get-account-reporting-events
summary: Account Reporting Events
security:
- userApiKey: []
description: Get paginated reporting events for the account. This endpoint returns reporting events such as first response time, resolution time, and other metrics. Only administrators can access this endpoint. Results are paginated with 25 items per page.
parameters:
- $ref: '#/components/parameters/page'
- in: query
name: since
schema:
type: string
description: The timestamp from where events should start (Unix timestamp in seconds)
- in: query
name: until
schema:
type: string
description: The timestamp from where events should stop (Unix timestamp in seconds)
- in: query
name: inbox_id
schema:
type: number
description: Filter events by inbox ID
- in: query
name: user_id
schema:
type: number
description: Filter events by user/agent ID
- in: query
name: name
schema:
type: string
description: Filter events by event name (e.g., first_response, resolution, reply_time)
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/reporting_events_list_response'
'403':
description: Access denied - Only administrators can access this endpoint
content:
application/json:
schema:
$ref: '#/components/schemas/bad_request_error'
+16
View File
@@ -389,6 +389,15 @@
post:
$ref: ./application/conversation/labels/create.yml
# Conversation Reporting Events
/api/v1/accounts/{account_id}/conversations/{conversation_id}/reporting_events:
parameters:
- $ref: '#/components/parameters/account_id'
- $ref: '#/components/parameters/conversation_id'
get:
$ref: ./application/conversation/reporting_events.yml
# Inboxes
/api/v1/accounts/{account_id}/inboxes:
$ref: ./application/inboxes/index.yml
@@ -535,6 +544,13 @@
### Reports
# Account Reporting Events
/api/v1/accounts/{account_id}/reporting_events:
parameters:
- $ref: '#/components/parameters/account_id'
get:
$ref: ./application/reporting_events/index.yml
# List
/api/v2/accounts/{account_id}/reports:
parameters:
+248
View File
@@ -5140,6 +5140,65 @@
}
}
},
"/api/v1/accounts/{account_id}/conversations/{conversation_id}/reporting_events": {
"parameters": [
{
"$ref": "#/components/parameters/account_id"
},
{
"$ref": "#/components/parameters/conversation_id"
}
],
"get": {
"tags": [
"Conversations"
],
"operationId": "get-conversation-reporting-events",
"summary": "Conversation Reporting Events",
"security": [
{
"userApiKey": []
}
],
"description": "Get reporting events for a specific conversation. This endpoint returns events such as first response time, resolution time, and other metrics for the conversation, sorted by creation time in ascending order.",
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/reporting_event"
},
"description": "Array of reporting events for the conversation"
}
}
}
},
"403": {
"description": "Access denied",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/bad_request_error"
}
}
}
},
"404": {
"description": "Conversation not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/bad_request_error"
}
}
}
}
}
}
},
"/api/v1/accounts/{account_id}/inboxes": {
"get": {
"tags": [
@@ -7311,6 +7370,93 @@
}
}
},
"/api/v1/accounts/{account_id}/reporting_events": {
"parameters": [
{
"$ref": "#/components/parameters/account_id"
}
],
"get": {
"tags": [
"Reports"
],
"operationId": "get-account-reporting-events",
"summary": "Account Reporting Events",
"security": [
{
"userApiKey": []
}
],
"description": "Get paginated reporting events for the account. This endpoint returns reporting events such as first response time, resolution time, and other metrics. Only administrators can access this endpoint. Results are paginated with 25 items per page.",
"parameters": [
{
"$ref": "#/components/parameters/page"
},
{
"in": "query",
"name": "since",
"schema": {
"type": "string"
},
"description": "The timestamp from where events should start (Unix timestamp in seconds)"
},
{
"in": "query",
"name": "until",
"schema": {
"type": "string"
},
"description": "The timestamp from where events should stop (Unix timestamp in seconds)"
},
{
"in": "query",
"name": "inbox_id",
"schema": {
"type": "number"
},
"description": "Filter events by inbox ID"
},
{
"in": "query",
"name": "user_id",
"schema": {
"type": "number"
},
"description": "Filter events by user/agent ID"
},
{
"in": "query",
"name": "name",
"schema": {
"type": "string"
},
"description": "Filter events by event name (e.g., first_response, resolution, reply_time)"
}
],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/reporting_events_list_response"
}
}
}
},
"403": {
"description": "Access denied - Only administrators can access this endpoint",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/bad_request_error"
}
}
}
}
}
}
},
"/api/v2/accounts/{account_id}/reports": {
"parameters": [
{
@@ -8981,6 +9127,10 @@
"type": "string",
"description": "The url to which the events will be send"
},
"name": {
"type": "string",
"description": "The name of the webhook"
},
"subscriptions": {
"type": "array",
"items": {
@@ -10560,6 +10710,10 @@
"description": "The url where the events should be sent",
"example": "https://example.com/webhook"
},
"name": {
"type": "string",
"description": "The name of the webhook"
},
"subscriptions": {
"type": "array",
"items": {
@@ -12066,6 +12220,100 @@
"description": "List of contactable inboxes for the contact"
}
}
},
"reporting_event": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "ID of the reporting event"
},
"name": {
"type": "string",
"description": "Name of the event (e.g., first_response, resolution, reply_time)"
},
"value": {
"type": "number",
"format": "double",
"description": "Value of the metric in seconds"
},
"value_in_business_hours": {
"type": "number",
"format": "double",
"description": "Value of the metric in seconds, calculated only for business hours"
},
"event_start_time": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the event started"
},
"event_end_time": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the event ended"
},
"account_id": {
"type": "number",
"description": "ID of the account"
},
"conversation_id": {
"type": "number",
"nullable": true,
"description": "ID of the conversation"
},
"inbox_id": {
"type": "number",
"nullable": true,
"description": "ID of the inbox"
},
"user_id": {
"type": "number",
"nullable": true,
"description": "ID of the user/agent"
},
"created_at": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the reporting event was created"
},
"updated_at": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the reporting event was last updated"
}
}
},
"reporting_event_meta": {
"type": "object",
"properties": {
"count": {
"type": "integer",
"description": "Total number of reporting events"
},
"current_page": {
"type": "integer",
"description": "Current page number"
},
"total_pages": {
"type": "integer",
"description": "Total number of pages"
}
}
},
"reporting_events_list_response": {
"type": "object",
"properties": {
"meta": {
"$ref": "#/components/schemas/reporting_event_meta"
},
"payload": {
"type": "array",
"items": {
"$ref": "#/components/schemas/reporting_event"
},
"description": "List of reporting events"
}
}
}
},
"parameters": {
+248
View File
@@ -3683,6 +3683,65 @@
}
}
},
"/api/v1/accounts/{account_id}/conversations/{conversation_id}/reporting_events": {
"parameters": [
{
"$ref": "#/components/parameters/account_id"
},
{
"$ref": "#/components/parameters/conversation_id"
}
],
"get": {
"tags": [
"Conversations"
],
"operationId": "get-conversation-reporting-events",
"summary": "Conversation Reporting Events",
"security": [
{
"userApiKey": []
}
],
"description": "Get reporting events for a specific conversation. This endpoint returns events such as first response time, resolution time, and other metrics for the conversation, sorted by creation time in ascending order.",
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/reporting_event"
},
"description": "Array of reporting events for the conversation"
}
}
}
},
"403": {
"description": "Access denied",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/bad_request_error"
}
}
}
},
"404": {
"description": "Conversation not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/bad_request_error"
}
}
}
}
}
}
},
"/api/v1/accounts/{account_id}/inboxes": {
"get": {
"tags": [
@@ -5854,6 +5913,93 @@
}
}
},
"/api/v1/accounts/{account_id}/reporting_events": {
"parameters": [
{
"$ref": "#/components/parameters/account_id"
}
],
"get": {
"tags": [
"Reports"
],
"operationId": "get-account-reporting-events",
"summary": "Account Reporting Events",
"security": [
{
"userApiKey": []
}
],
"description": "Get paginated reporting events for the account. This endpoint returns reporting events such as first response time, resolution time, and other metrics. Only administrators can access this endpoint. Results are paginated with 25 items per page.",
"parameters": [
{
"$ref": "#/components/parameters/page"
},
{
"in": "query",
"name": "since",
"schema": {
"type": "string"
},
"description": "The timestamp from where events should start (Unix timestamp in seconds)"
},
{
"in": "query",
"name": "until",
"schema": {
"type": "string"
},
"description": "The timestamp from where events should stop (Unix timestamp in seconds)"
},
{
"in": "query",
"name": "inbox_id",
"schema": {
"type": "number"
},
"description": "Filter events by inbox ID"
},
{
"in": "query",
"name": "user_id",
"schema": {
"type": "number"
},
"description": "Filter events by user/agent ID"
},
{
"in": "query",
"name": "name",
"schema": {
"type": "string"
},
"description": "Filter events by event name (e.g., first_response, resolution, reply_time)"
}
],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/reporting_events_list_response"
}
}
}
},
"403": {
"description": "Access denied - Only administrators can access this endpoint",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/bad_request_error"
}
}
}
}
}
}
},
"/api/v2/accounts/{account_id}/reports": {
"parameters": [
{
@@ -7488,6 +7634,10 @@
"type": "string",
"description": "The url to which the events will be send"
},
"name": {
"type": "string",
"description": "The name of the webhook"
},
"subscriptions": {
"type": "array",
"items": {
@@ -9067,6 +9217,10 @@
"description": "The url where the events should be sent",
"example": "https://example.com/webhook"
},
"name": {
"type": "string",
"description": "The name of the webhook"
},
"subscriptions": {
"type": "array",
"items": {
@@ -10573,6 +10727,100 @@
"description": "List of contactable inboxes for the contact"
}
}
},
"reporting_event": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "ID of the reporting event"
},
"name": {
"type": "string",
"description": "Name of the event (e.g., first_response, resolution, reply_time)"
},
"value": {
"type": "number",
"format": "double",
"description": "Value of the metric in seconds"
},
"value_in_business_hours": {
"type": "number",
"format": "double",
"description": "Value of the metric in seconds, calculated only for business hours"
},
"event_start_time": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the event started"
},
"event_end_time": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the event ended"
},
"account_id": {
"type": "number",
"description": "ID of the account"
},
"conversation_id": {
"type": "number",
"nullable": true,
"description": "ID of the conversation"
},
"inbox_id": {
"type": "number",
"nullable": true,
"description": "ID of the inbox"
},
"user_id": {
"type": "number",
"nullable": true,
"description": "ID of the user/agent"
},
"created_at": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the reporting event was created"
},
"updated_at": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the reporting event was last updated"
}
}
},
"reporting_event_meta": {
"type": "object",
"properties": {
"count": {
"type": "integer",
"description": "Total number of reporting events"
},
"current_page": {
"type": "integer",
"description": "Current page number"
},
"total_pages": {
"type": "integer",
"description": "Total number of pages"
}
}
},
"reporting_events_list_response": {
"type": "object",
"properties": {
"meta": {
"$ref": "#/components/schemas/reporting_event_meta"
},
"payload": {
"type": "array",
"items": {
"$ref": "#/components/schemas/reporting_event"
},
"description": "List of reporting events"
}
}
}
},
"parameters": {
+102
View File
@@ -1934,6 +1934,10 @@
"type": "string",
"description": "The url to which the events will be send"
},
"name": {
"type": "string",
"description": "The name of the webhook"
},
"subscriptions": {
"type": "array",
"items": {
@@ -3513,6 +3517,10 @@
"description": "The url where the events should be sent",
"example": "https://example.com/webhook"
},
"name": {
"type": "string",
"description": "The name of the webhook"
},
"subscriptions": {
"type": "array",
"items": {
@@ -5019,6 +5027,100 @@
"description": "List of contactable inboxes for the contact"
}
}
},
"reporting_event": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "ID of the reporting event"
},
"name": {
"type": "string",
"description": "Name of the event (e.g., first_response, resolution, reply_time)"
},
"value": {
"type": "number",
"format": "double",
"description": "Value of the metric in seconds"
},
"value_in_business_hours": {
"type": "number",
"format": "double",
"description": "Value of the metric in seconds, calculated only for business hours"
},
"event_start_time": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the event started"
},
"event_end_time": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the event ended"
},
"account_id": {
"type": "number",
"description": "ID of the account"
},
"conversation_id": {
"type": "number",
"nullable": true,
"description": "ID of the conversation"
},
"inbox_id": {
"type": "number",
"nullable": true,
"description": "ID of the inbox"
},
"user_id": {
"type": "number",
"nullable": true,
"description": "ID of the user/agent"
},
"created_at": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the reporting event was created"
},
"updated_at": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the reporting event was last updated"
}
}
},
"reporting_event_meta": {
"type": "object",
"properties": {
"count": {
"type": "integer",
"description": "Total number of reporting events"
},
"current_page": {
"type": "integer",
"description": "Current page number"
},
"total_pages": {
"type": "integer",
"description": "Total number of pages"
}
}
},
"reporting_events_list_response": {
"type": "object",
"properties": {
"meta": {
"$ref": "#/components/schemas/reporting_event_meta"
},
"payload": {
"type": "array",
"items": {
"$ref": "#/components/schemas/reporting_event"
},
"description": "List of reporting events"
}
}
}
},
"parameters": {
+102
View File
@@ -1349,6 +1349,10 @@
"type": "string",
"description": "The url to which the events will be send"
},
"name": {
"type": "string",
"description": "The name of the webhook"
},
"subscriptions": {
"type": "array",
"items": {
@@ -2928,6 +2932,10 @@
"description": "The url where the events should be sent",
"example": "https://example.com/webhook"
},
"name": {
"type": "string",
"description": "The name of the webhook"
},
"subscriptions": {
"type": "array",
"items": {
@@ -4434,6 +4442,100 @@
"description": "List of contactable inboxes for the contact"
}
}
},
"reporting_event": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "ID of the reporting event"
},
"name": {
"type": "string",
"description": "Name of the event (e.g., first_response, resolution, reply_time)"
},
"value": {
"type": "number",
"format": "double",
"description": "Value of the metric in seconds"
},
"value_in_business_hours": {
"type": "number",
"format": "double",
"description": "Value of the metric in seconds, calculated only for business hours"
},
"event_start_time": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the event started"
},
"event_end_time": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the event ended"
},
"account_id": {
"type": "number",
"description": "ID of the account"
},
"conversation_id": {
"type": "number",
"nullable": true,
"description": "ID of the conversation"
},
"inbox_id": {
"type": "number",
"nullable": true,
"description": "ID of the inbox"
},
"user_id": {
"type": "number",
"nullable": true,
"description": "ID of the user/agent"
},
"created_at": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the reporting event was created"
},
"updated_at": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the reporting event was last updated"
}
}
},
"reporting_event_meta": {
"type": "object",
"properties": {
"count": {
"type": "integer",
"description": "Total number of reporting events"
},
"current_page": {
"type": "integer",
"description": "Current page number"
},
"total_pages": {
"type": "integer",
"description": "Total number of pages"
}
}
},
"reporting_events_list_response": {
"type": "object",
"properties": {
"meta": {
"$ref": "#/components/schemas/reporting_event_meta"
},
"payload": {
"type": "array",
"items": {
"$ref": "#/components/schemas/reporting_event"
},
"description": "List of reporting events"
}
}
}
},
"parameters": {
+102
View File
@@ -2110,6 +2110,10 @@
"type": "string",
"description": "The url to which the events will be send"
},
"name": {
"type": "string",
"description": "The name of the webhook"
},
"subscriptions": {
"type": "array",
"items": {
@@ -3689,6 +3693,10 @@
"description": "The url where the events should be sent",
"example": "https://example.com/webhook"
},
"name": {
"type": "string",
"description": "The name of the webhook"
},
"subscriptions": {
"type": "array",
"items": {
@@ -5195,6 +5203,100 @@
"description": "List of contactable inboxes for the contact"
}
}
},
"reporting_event": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "ID of the reporting event"
},
"name": {
"type": "string",
"description": "Name of the event (e.g., first_response, resolution, reply_time)"
},
"value": {
"type": "number",
"format": "double",
"description": "Value of the metric in seconds"
},
"value_in_business_hours": {
"type": "number",
"format": "double",
"description": "Value of the metric in seconds, calculated only for business hours"
},
"event_start_time": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the event started"
},
"event_end_time": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the event ended"
},
"account_id": {
"type": "number",
"description": "ID of the account"
},
"conversation_id": {
"type": "number",
"nullable": true,
"description": "ID of the conversation"
},
"inbox_id": {
"type": "number",
"nullable": true,
"description": "ID of the inbox"
},
"user_id": {
"type": "number",
"nullable": true,
"description": "ID of the user/agent"
},
"created_at": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the reporting event was created"
},
"updated_at": {
"type": "string",
"format": "date-time",
"description": "The timestamp when the reporting event was last updated"
}
}
},
"reporting_event_meta": {
"type": "object",
"properties": {
"count": {
"type": "integer",
"description": "Total number of reporting events"
},
"current_page": {
"type": "integer",
"description": "Current page number"
},
"total_pages": {
"type": "integer",
"description": "Total number of pages"
}
}
},
"reporting_events_list_response": {
"type": "object",
"properties": {
"meta": {
"$ref": "#/components/schemas/reporting_event_meta"
},
"payload": {
"type": "array",
"items": {
"$ref": "#/components/schemas/reporting_event"
},
"description": "List of reporting events"
}
}
}
},
"parameters": {